diff --git a/api-reference/github-tutorials.mdx b/api-reference/github-tutorials.mdx
new file mode 100644
index 0000000000..8b7da7e89e
--- /dev/null
+++ b/api-reference/github-tutorials.mdx
@@ -0,0 +1,23 @@
+---
+title: "GitHub Tutorials"
+description: "Explore our collection of Python and TypeScript tutorials on GitHub."
+---
+
+# GitHub Tutorials
+
+For a hands-on approach to learning Prem, we maintain a dedicated repository of tutorials and examples.
+
+
+ Access Python and TypeScript scripts, example datasets, and complete workflows.
+
+
+## What you will find
+
+This repository contains practical examples demonstrating how to use the Prem API with:
+
+- **Python Scripts**: Ready-to-run scripts for various use cases.
+- **TypeScript Examples**: Code snippets and projects for JavaScript/TypeScript developers.
+- **Example Datasets**: Real-world data to test and experiment with.
+- **End-to-End Workflows**: Complete examples showing how to integrate Prem into your applications.
+
+These resources are designed to help you get started quickly and understand the full capabilities of the Prem platform.
diff --git a/api-reference/introduction.mdx b/api-reference/introduction.mdx
index b92b712162..99ddcd9da2 100644
--- a/api-reference/introduction.mdx
+++ b/api-reference/introduction.mdx
@@ -17,38 +17,6 @@ Here you can find documentation for all of the endpoints that are available in t
**Prem** provides a REST API that you can use to interact with the platform with any programming language that can make HTTP requests.
-## š End-to-End Walkthroughs
-
-New to Prem? Start here! These comprehensive walkthroughs guide you through complete workflows from data ingestion to fine-tuning, showing you how to use multiple API endpoints together.
-
-
-
- Upload a JSONL dataset, create snapshots, and launch fine-tuning jobs with your own data.
-
-
-
- Generate synthetic Q&A pairs from PDF documents and fine-tune models on corporate knowledge.
-
-
-
- Transform YouTube video content into training data and create specialized models.
-
-
-
----
-
## API Endpoints
#### Build your data pipeline
diff --git a/api-reference/walkthroughs/continuous-finetuning-workflow.mdx b/api-reference/walkthroughs/continuous-finetuning-workflow.mdx
deleted file mode 100644
index 60f802e9e8..0000000000
--- a/api-reference/walkthroughs/continuous-finetuning-workflow.mdx
+++ /dev/null
@@ -1,1018 +0,0 @@
----
-title: "Continuous Fine-Tuning"
-description: "Use traces to continuously improve your models by evaluating responses and iteratively fine-tuning."
----
-
-## Overview
-
-Continuously improve your fine-tuned model by:
-- Testing with domain-specific prompts
-- Evaluating responses with a judge model (Claude 4.5 Sonnet)
-- Creating traces with scores and feedback
-- Adding all traces to your dataset (low-scoring traces are automatically improved)
-- Launching new fine-tuning jobs that learn from both successes and corrected failures
-
-
- **CRITICAL**: This workflow requires you to provide test prompts that match your fine-tuned model's domain. Generic prompts will produce useless results. For example:
- - **Invoice model?** Test with actual invoice text
- - **Stock analysis model?** Test with financial questions/transcripts
- - **Custom domain?** Test with prompts from YOUR specific use case
-
-
-
- **Prerequisites**: This walkthrough assumes you have already completed one of the previous workflows ([JSONL](/api-reference/walkthroughs/jsonl-workflow), [PDF](/api-reference/walkthroughs/pdf-synthetic-workflow), or [YouTube](/api-reference/walkthroughs/youtube-synthetic-workflow)) and have:
- - A project with at least one fine-tuned model
- - An existing dataset
- - A fine-tuned model ID/alias ready for inference
-
- Export your Prem API key as `API_KEY` before running any script.
-
-
-
-
-
- # Setup
-
-
- ```ts TypeScript
- const PROJECT_ID = 'your-project-id-here';
- const FINETUNED_MODEL_ALIAS = 'your-model-alias';
- const JUDGE_MODEL = 'claude-4.5-sonnet';
-
- // Fetch dataset from project
- const projectRes = await fetch(`https://studio.premai.io/api/v1/public/projects/${PROJECT_ID}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!projectRes.ok) throw new Error(`Failed to fetch project: ${projectRes.status}`);
- const project = await projectRes.json();
- const dataset = project.project.children.find((child: any) => child.type === 'dataset');
- if (!dataset) throw new Error('No dataset found in project');
- const DATASET_ID = dataset.id;
-
- // CRITICAL: Replace with prompts matching your model's domain!
- const TEST_PROMPTS = [
- 'YOUR_FIRST_TEST_PROMPT_HERE - MUST match your model domain!',
- 'YOUR_SECOND_TEST_PROMPT_HERE - MUST match your model domain!',
- 'YOUR_THIRD_TEST_PROMPT_HERE - MUST match your model domain!'
- ];
- ```
- ```python Python
- PROJECT_ID = "your-project-id-here"
- FINETUNED_MODEL_ALIAS = "your-model-alias"
- JUDGE_MODEL = "claude-4.5-sonnet"
-
- # Fetch dataset from project
- project = api(f"/api/v1/public/projects/{PROJECT_ID}")
- dataset = next((child for child in project["project"]["children"] if child["type"] == "dataset"), None)
- if not dataset:
- raise Exception("No dataset found in project")
- DATASET_ID = dataset["id"]
-
- # CRITICAL: Replace with prompts matching your model's domain!
- TEST_PROMPTS = [
- "YOUR_FIRST_TEST_PROMPT_HERE - MUST match your model domain!",
- "YOUR_SECOND_TEST_PROMPT_HERE - MUST match your model domain!",
- "YOUR_THIRD_TEST_PROMPT_HERE - MUST match your model domain!"
- ]
- ```
-
-
- Fetch project details to get the dataset ID. See [Get Project](/api-reference/projects/get-projects-projectid) for details.
-
-
-
- # Test model with prompts
-
- Generate responses from your fine-tuned model using test prompts. See [Chat Completions](/api-reference/chat-completions/post-chatcompletions) for details.
-
-
- ```ts TypeScript
- const modelResponses = [];
-
- for (const prompt of TEST_PROMPTS) {
- const res = await fetch('https://studio.premai.io/api/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- project_id: PROJECT_ID,
- model: FINETUNED_MODEL_ALIAS,
- messages: [{ role: 'user', content: prompt }]
- })
- });
-
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const response = await res.json();
- modelResponses.push({
- prompt,
- answer: response.choices[0].message.content
- });
- }
- ```
- ```python Python
- import requests
- import json
- import time
- import os
-
- API_KEY = os.getenv("API_KEY")
-
- if not API_KEY:
- print("Error: API_KEY environment variable is required")
- exit(1)
-
-
- def api(endpoint: str, method: str = "GET", **kwargs):
- response = requests.request(
- method=method,
- url=f"https://studio.premai.io{endpoint}",
- headers={"Authorization": f"Bearer {API_KEY}", **kwargs.pop("headers", {})},
- **kwargs
- )
- if not response.ok:
- err = response.json() if response.content else {}
- error_msg = err.get("error", str(err)) if isinstance(err, dict) else str(err)
- raise Exception(f"{response.status_code}: {error_msg}")
- return response.json()
-
-
- model_responses = []
-
- for prompt in TEST_PROMPTS:
- response = api(
- "/api/v1/chat/completions",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "project_id": PROJECT_ID,
- "model": FINETUNED_MODEL_ALIAS,
- "messages": [{"role": "user", "content": prompt}]
- }
- )
- model_responses.append({
- "prompt": prompt,
- "answer": response["choices"][0]["message"]["content"]
- })
- ```
-
-
-
-
- # Score responses with judge
-
- Use a judge model to evaluate response quality. See [Chat Completions](/api-reference/chat-completions/post-chatcompletions) for details.
-
-
- ```ts TypeScript
- const evaluations = [];
-
- for (const item of modelResponses) {
- const judgePrompt = `You are an expert AI evaluator. Evaluate the following response.
-
-User Question: ${item.prompt}
-
-Model Response: ${item.answer}
-
-Provide your evaluation in the following JSON format (output ONLY the JSON, no other text):
-{
- "feedback": "",
- "reasoning": "",
- "score":
-}`;
-
- const res = await fetch('https://studio.premai.io/api/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- project_id: PROJECT_ID,
- model: JUDGE_MODEL,
- messages: [{ role: 'user', content: judgePrompt }],
- temperature: 0.1
- })
- });
-
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const response = await res.json();
- const judgeResponse = response.choices[0].message.content;
-
- let evaluation;
- try {
- const jsonMatch = judgeResponse.match(/\{[\s\S]*\}/);
- evaluation = JSON.parse(jsonMatch ? jsonMatch[0] : judgeResponse);
- } catch (e) {
- evaluation = {
- score: 0.5,
- feedback: judgeResponse,
- reasoning: 'Could not parse structured evaluation'
- };
- }
-
- evaluations.push({
- prompt: item.prompt,
- answer: item.answer,
- score: evaluation.score,
- feedback: evaluation.feedback,
- reasoning: evaluation.reasoning
- });
- }
- ```
- ```python Python
- evaluations = []
-
- for item in model_responses:
- judge_prompt = f"""You are an expert AI evaluator. Evaluate the following response.
-
-User Question: {item['prompt']}
-
-Model Response: {item['answer']}
-
-Provide your evaluation in the following JSON format (output ONLY the JSON, no other text):
-{{
- "feedback": "",
- "reasoning": "",
- "score":
-}}"""
-
- response = api(
- "/api/v1/chat/completions",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "project_id": PROJECT_ID,
- "model": JUDGE_MODEL,
- "messages": [{"role": "user", "content": judge_prompt}],
- "temperature": 0.1
- }
- )
-
- judge_response = response["choices"][0]["message"]["content"]
-
- try:
- import re
- json_match = re.search(r'\{[\s\S]*\}', judge_response)
- evaluation = json.loads(json_match.group(0) if json_match else judge_response)
- except Exception:
- evaluation = {
- "score": 0.5,
- "feedback": judge_response,
- "reasoning": "Could not parse structured evaluation"
- }
-
- evaluations.append({
- "prompt": item["prompt"],
- "answer": item["answer"],
- "score": evaluation["score"],
- "feedback": evaluation["feedback"],
- "reasoning": evaluation["reasoning"]
- })
- ```
-
-
-
-
- # Create traces with scores
-
- Create traces with evaluation scores and feedback. See [Create Trace](/api-reference/traces/post-traces) for details.
-
-
- ```ts TypeScript
- const traceIds = [];
-
- for (const evaluation of evaluations) {
- const res = await fetch('https://studio.premai.io/api/v1/traces', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- project_id: PROJECT_ID,
- model_id: FINETUNED_MODEL_ALIAS,
- input: evaluation.prompt,
- output: evaluation.answer,
- score: evaluation.score,
- feedback: `${evaluation.feedback}\n\nReasoning: ${evaluation.reasoning}`
- })
- });
-
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const trace = await res.json();
- traceIds.push(trace.id);
- }
- ```
- ```python Python
- trace_ids = []
-
- for evaluation in evaluations:
- trace = api(
- "/api/v1/traces",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "project_id": PROJECT_ID,
- "model_id": FINETUNED_MODEL_ALIAS,
- "input": evaluation["prompt"],
- "output": evaluation["answer"],
- "score": evaluation["score"],
- "feedback": f"{evaluation['feedback']}\n\nReasoning: {evaluation['reasoning']}"
- }
- )
- trace_ids.append(trace["id"])
- ```
-
-
-
-
- # Add traces to dataset
-
-
- ```ts TypeScript
- for (const traceId of traceIds) {
- const res = await fetch(`https://studio.premai.io/api/v1/traces/${traceId}/addToDataset`, {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`Failed to add trace: ${res.status}`);
- }
- ```
- ```python Python
- for trace_id in trace_ids:
- response = requests.post(
- f"https://studio.premai.io/api/v1/traces/{trace_id}/addToDataset",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- ```
-
-
- Add traces to your dataset. Low-scoring traces are automatically improved using judge feedback. See [Add Trace to Dataset](/api-reference/traces/post-trace-add-to-dataset) for details.
-
-
-
- # Create snapshot
-
- Create a snapshot with train/validation split. See [Create Snapshot](/api-reference/snapshots/post-snapshots-create) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- dataset_id: DATASET_ID,
- split_percentage: 80
- })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { snapshot_id } = await res.json();
- ```
- ```python Python
- result = api(
- "/api/v1/public/snapshots/create",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={"dataset_id": DATASET_ID, "split_percentage": 80}
- )
- snapshot_id = result["snapshot_id"]
- ```
-
-
-
-
- # Get model recommendations
-
- Generate and retrieve model recommendations for your snapshot. See [Generate Recommendations](/api-reference/recommendations/post-recommendations-generate) and [Get Recommendations](/api-reference/recommendations/get-recommendations-snapshotid) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res2 = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- recs = await res2.json();
- } while (recs.status === 'processing');
- ```
- ```python Python
- api("/api/v1/public/recommendations/generate", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id})
-
- while True:
- time.sleep(5)
- recs = api(f"/api/v1/public/recommendations/{snapshot_id}")
- if recs["status"] != "processing":
- break
- ```
-
-
-
-
- # Start fine-tuning job
-
- Launch a fine-tuning job with recommended experiments. See [Create Fine-Tuning Job](/api-reference/finetuning/post-finetuning-create) for details.
-
-
- ```ts TypeScript
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- if (experiments.length === 0) throw new Error('No recommended experiments found');
-
- const res = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- snapshot_id,
- name: `Continuous Fine-tuning - ${new Date().toISOString().split('T')[0]}`,
- experiments
- })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { job_id } = await res.json();
- ```
- ```python Python
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- if not experiments:
- raise Exception("No recommended experiments found")
-
- from datetime import datetime
- result = api(
- "/api/v1/public/finetuning/create",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "snapshot_id": snapshot_id,
- "name": f"Continuous Fine-tuning - {datetime.now().strftime('%Y-%m-%d')}",
- "experiments": experiments
- }
- )
- job_id = result["job_id"]
- ```
-
-
-
-
-## Full Example
-
-
-```ts TypeScript
-#!/usr/bin/env bun
-
-const API_KEY = process.env.API_KEY;
-const PROJECT_ID = 'your-project-id-here';
-const FINETUNED_MODEL_ALIAS = 'your-model-alias';
-const JUDGE_MODEL = 'claude-4.5-sonnet';
-
-// CRITICAL: Replace with prompts matching your model's domain!
-const TEST_PROMPTS = [
- 'YOUR_FIRST_TEST_PROMPT_HERE',
- 'YOUR_SECOND_TEST_PROMPT_HERE',
- 'YOUR_THIRD_TEST_PROMPT_HERE'
-];
-
-if (!API_KEY) {
- console.error('Error: API_KEY environment variable is required');
- process.exit(1);
-}
-
-function sleep(ms: number) {
- return new Promise((r) => setTimeout(r, ms));
-}
-
-async function main() {
- const projectRes = await fetch(`https://studio.premai.io/api/v1/public/projects/${PROJECT_ID}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!projectRes.ok) throw new Error(`Failed to fetch project: ${projectRes.status}`);
- const project = await projectRes.json();
- const dataset = project.project.children.find((child: any) => child.type === 'dataset');
- if (!dataset) throw new Error('No dataset found in project');
- const DATASET_ID = dataset.id;
-
- const modelResponses = [];
- for (const prompt of TEST_PROMPTS) {
- const res = await fetch('https://studio.premai.io/api/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- project_id: PROJECT_ID,
- model: FINETUNED_MODEL_ALIAS,
- messages: [{ role: 'user', content: prompt }]
- })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const response = await res.json();
- modelResponses.push({ prompt, answer: response.choices[0].message.content });
- }
-
- const evaluations = [];
-
- for (const item of modelResponses) {
- const judgePrompt = `You are an expert AI evaluator. Evaluate the following response.
-
-User Question: ${item.prompt}
-
-Model Response: ${item.answer}
-
-Provide your evaluation in the following JSON format (output ONLY the JSON, no other text):
-{
- "feedback": "",
- "reasoning": "",
- "score":
-}`;
-
- const res = await fetch('https://studio.premai.io/api/v1/chat/completions', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- project_id: PROJECT_ID,
- model: JUDGE_MODEL,
- messages: [{ role: 'user', content: judgePrompt }],
- temperature: 0.1
- })
- });
-
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const response = await res.json();
- const judgeResponse = response.choices[0].message.content;
-
- let evaluation;
- try {
- const jsonMatch = judgeResponse.match(/\{[\s\S]*\}/);
- evaluation = JSON.parse(jsonMatch ? jsonMatch[0] : judgeResponse);
- } catch (e) {
- console.warn(`Warning: Could not parse judge response`);
- evaluation = {
- score: 5,
- feedback: judgeResponse,
- reasoning: 'Could not parse structured evaluation'
- };
- }
-
- evaluations.push({
- prompt: item.prompt,
- answer: item.answer,
- score: evaluation.score,
- feedback: evaluation.feedback,
- reasoning: evaluation.reasoning
- });
-
- console.log(`ā Evaluated: "${item.prompt.substring(0, 40)}..." - Score: ${evaluation.score}`);
- }
-
- console.log(`\nā Evaluated ${evaluations.length} responses\n`);
-
- // Step 3: Create traces
- console.log('=== Step 3: Creating traces with evaluation data ===\n');
-
- const traceIds = [];
-
- for (const evaluation of evaluations) {
- const res = await fetch('https://studio.premai.io/api/v1/traces', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- project_id: PROJECT_ID,
- model_id: FINETUNED_MODEL_ALIAS,
- input: evaluation.prompt,
- output: evaluation.answer,
- score: evaluation.score,
- feedback: `${evaluation.feedback}\n\nReasoning: ${evaluation.reasoning}`
- })
- });
-
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const trace = await res.json();
-
- traceIds.push(trace.id);
- console.log(`ā Created trace ${trace.id} - Score: ${evaluation.score}`);
- }
-
- console.log(`\nā Created ${traceIds.length} traces\n`);
-
- // Step 4: Add ALL traces to dataset (including low-scoring ones!)
- console.log('=== Step 4: Adding ALL traces to dataset ===\n');
- console.log('IMPORTANT: Adding ALL traces, including low-scoring ones!');
- console.log('Low-scoring traces help identify model weaknesses.');
- console.log('The addToDataset endpoint automatically uses the dataset from the project.\n');
-
- const addedTraces = [];
-
- for (let i = 0; i < traceIds.length; i++) {
- const traceId = traceIds[i];
- const score = evaluations[i].score;
-
- const res = await fetch(`https://studio.premai.io/api/v1/traces/${traceId}/addToDataset`, {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`
- }
- });
-
- if (!res.ok) {
- console.warn(`Warning: Failed to add trace ${traceId}`);
- continue;
- }
-
- addedTraces.push(traceId);
- const quality = score >= 0.7 ? 'high-quality' : 'low-quality (will be improved)';
- console.log(`ā Added trace ${traceId} (${quality}, score: ${score}) to dataset`);
- }
-
- console.log(`\nā Added ${addedTraces.length} traces to dataset\n`);
-
- // Step 5: Create new snapshot
- console.log('=== Step 5: Creating new snapshot ===\n');
-
- const res5 = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- dataset_id: DATASET_ID,
- split_percentage: 80
- })
- });
-
- if (!res5.ok) throw new Error(`${res5.status}: ${await res5.text()}`);
- const { snapshot_id } = await res5.json();
-
- console.log(`ā Created new snapshot: ${snapshot_id}\n`);
-
- // Step 6: Generate recommendations
- console.log('=== Step 6: Generating recommendations ===\n');
-
- const res6 = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id })
- });
-
- if (!res6.ok) throw new Error(`${res6.status}: ${await res6.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- recs = await res.json();
- } while (recs.status === 'processing');
-
- console.log('ā Recommendations ready\n');
-
- // Step 7: Launch new fine-tuning job
- console.log('=== Step 7: Launching new fine-tuning job ===\n');
-
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- if (experiments.length === 0) {
- console.error('ā No recommended experiments found');
- process.exit(1);
- }
-
- const res7 = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- snapshot_id,
- name: `Continuous Fine-tuning - ${new Date().toISOString().split('T')[0]}`,
- experiments
- })
- });
-
- if (!res7.ok) throw new Error(`${res7.status}: ${await res7.text()}`);
- const { job_id } = await res7.json();
-
- console.log(`ā Fine-tuning job started: ${job_id}\n`);
- console.log('ā Continuous fine-tuning cycle complete!\n');
-}
-
-main().catch((err) => {
- console.error('\nā Error:', err.message);
- process.exit(1);
-});
-```
-```python Python
-#!/usr/bin/env python3
-
-"""
-Continuous Fine-Tuning with Traces Workflow
-
-Prerequisites: You must have completed a previous workflow and have:
-- PROJECT_ID: Your existing project ID
-- FINETUNED_MODEL_ALIAS: Your fine-tuned model alias
-(DATASET_ID is automatically fetched from the project)
-"""
-
-import os
-import time
-import requests
-import json
-import re
-from datetime import datetime
-
-API_KEY = os.getenv("API_KEY")
-
-# ============================================
-# IMPORTANT: Replace these with YOUR actual IDs
-# ============================================
-PROJECT_ID = "your-project-id-here"
-FINETUNED_MODEL_ALIAS = "your-model-alias"
-JUDGE_MODEL = "claude-4.5-sonnet"
-
-# ============================================
-# CRITICAL: Replace with prompts matching YOUR model domain!
-# ============================================
-# Invoice model example: test with actual invoice text
-# Stock model example: test with financial analysis questions
-# Your domain: YOUR specific use case prompts
-TEST_PROMPTS = [
- "YOUR_FIRST_TEST_PROMPT_HERE - MUST match your model domain!",
- "YOUR_SECOND_TEST_PROMPT_HERE - MUST match your model domain!",
- "YOUR_THIRD_TEST_PROMPT_HERE - MUST match your model domain!"
-]
-
-if not API_KEY:
- print("Error: API_KEY environment variable is required")
- exit(1)
-
-
-def api(endpoint: str, method: str = "GET", **kwargs):
- response = requests.request(
- method=method,
- url=f"https://studio.premai.io{endpoint}",
- headers={"Authorization": f"Bearer {API_KEY}", **kwargs.pop("headers", {})},
- **kwargs
- )
- if not response.ok:
- err = response.json() if response.content else {}
- error_msg = err.get("error", str(err)) if isinstance(err, dict) else str(err)
- raise Exception(f"{response.status_code}: {error_msg}")
- return response.json()
-
-
-def main():
- project = api(f"/api/v1/public/projects/{PROJECT_ID}")
- dataset = next((child for child in project["project"]["children"] if child["type"] == "dataset"), None)
- if not dataset:
- raise Exception("No dataset found in project")
- DATASET_ID = dataset["id"]
-
- # Step 1: Generate responses from fine-tuned model
- print("=== Step 1: Generating responses from fine-tuned model ===\n")
-
- model_responses = []
-
- for prompt in TEST_PROMPTS:
- response = api(
- "/api/v1/chat/completions",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "project_id": PROJECT_ID,
- "model": FINETUNED_MODEL_ALIAS,
- "messages": [{"role": "user", "content": prompt}]
- }
- )
-
- model_answer = response["choices"][0]["message"]["content"]
- model_responses.append({"prompt": prompt, "answer": model_answer})
- print(f"ā Generated response for: \"{prompt[:50]}...\"")
-
- print(f"\nā Generated {len(model_responses)} responses\n")
-
- # Step 2: Evaluate responses with judge model
- print("=== Step 2: Evaluating responses with Claude 4.5 Sonnet ===\n")
-
- evaluations = []
-
- for item in model_responses:
- judge_prompt = f"""You are an expert AI evaluator. Evaluate the following response.
-
-User Question: {item['prompt']}
-
-Model Response: {item['answer']}
-
-Provide your evaluation in the following JSON format (output ONLY the JSON, no other text):
-{{
- "feedback": "",
- "reasoning": "",
- "score":
-}}"""
-
- response = api(
- "/api/v1/chat/completions",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "project_id": PROJECT_ID,
- "model": JUDGE_MODEL,
- "messages": [{"role": "user", "content": judge_prompt}],
- "temperature": 0.1
- }
- )
-
- judge_response = response["choices"][0]["message"]["content"]
-
- try:
- json_match = re.search(r'\{[\s\S]*\}', judge_response)
- evaluation = json.loads(json_match.group(0) if json_match else judge_response)
- except Exception as e:
- print(f"Warning: Could not parse judge response")
- evaluation = {
- "score": 5,
- "feedback": judge_response,
- "reasoning": "Could not parse structured evaluation"
- }
-
- evaluations.append({
- "prompt": item["prompt"],
- "answer": item["answer"],
- "score": evaluation["score"],
- "feedback": evaluation["feedback"],
- "reasoning": evaluation["reasoning"]
- })
-
- print(f"ā Evaluated: \"{item['prompt'][:40]}...\" - Score: {evaluation['score']}")
-
- print(f"\nā Evaluated {len(evaluations)} responses\n")
-
- # Step 3: Create traces
- print("=== Step 3: Creating traces with evaluation data ===\n")
-
- trace_ids = []
-
- for evaluation in evaluations:
- trace = api(
- "/api/v1/traces",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "project_id": PROJECT_ID,
- "model_id": FINETUNED_MODEL_ALIAS,
- "input": evaluation["prompt"],
- "output": evaluation["answer"],
- "score": evaluation["score"],
- "feedback": f"{evaluation['feedback']}\n\nReasoning: {evaluation['reasoning']}"
- }
- )
-
- trace_ids.append(trace["id"])
- print(f"ā Created trace {trace['id']} - Score: {evaluation['score']}")
-
- print(f"\nā Created {len(trace_ids)} traces\n")
-
- # Step 4: Add ALL traces to dataset (including low-scoring ones!)
- print("=== Step 4: Adding ALL traces to dataset ===\n")
- print("IMPORTANT: Adding ALL traces, including low-scoring ones!")
- print("Low-scoring traces help identify model weaknesses.")
- print("The addToDataset endpoint automatically uses the dataset from the project.\n")
-
- added_traces = []
-
- for i, trace_id in enumerate(trace_ids):
- score = evaluations[i]["score"]
-
- try:
- # Note: addToDataset does NOT take parameters - it automatically uses the dataset from the project
- response = requests.post(
- f"https://studio.premai.io/api/v1/traces/{trace_id}/addToDataset",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
-
- added_traces.append(trace_id)
- quality = "high-quality" if score >= 0.7 else "low-quality (will be improved)"
- print(f"ā Added trace {trace_id} ({quality}, score: {score}) to dataset")
- except Exception as e:
- print(f"Warning: Failed to add trace {trace_id}")
- continue
-
- print(f"\nā Added {len(added_traces)} traces to dataset\n")
-
- # Step 5: Create new snapshot
- print("=== Step 5: Creating new snapshot ===\n")
-
- result = api(
- "/api/v1/public/snapshots/create",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "dataset_id": DATASET_ID,
- "split_percentage": 80
- }
- )
-
- snapshot_id = result["snapshot_id"]
- print(f"ā Created new snapshot: {snapshot_id}\n")
-
- # Step 6: Generate recommendations
- print("=== Step 6: Generating recommendations ===\n")
-
- api(
- "/api/v1/public/recommendations/generate",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id}
- )
-
- while True:
- time.sleep(5)
- recs = api(f"/api/v1/public/recommendations/{snapshot_id}")
- if recs["status"] != "processing":
- break
-
- print("ā Recommendations ready\n")
-
- # Step 7: Launch new fine-tuning job
- print("=== Step 7: Launching new fine-tuning job ===\n")
-
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- if not experiments:
- print("ā No recommended experiments found")
- exit(1)
-
- result = api(
- "/api/v1/public/finetuning/create",
- method="POST",
- headers={"Content-Type": "application/json"},
- json={
- "snapshot_id": snapshot_id,
- "name": f"Continuous Fine-tuning - {datetime.now().strftime('%Y-%m-%d')}",
- "experiments": experiments
- }
- )
-
- job_id = result["job_id"]
-
- print(f"ā Fine-tuning job started: {job_id}\n")
- print("ā Continuous fine-tuning cycle complete!\n")
-
-
-if __name__ == "__main__":
- try:
- main()
- except Exception as err:
- print(f"\nā Error: {err}")
- exit(1)
-```
-
-
-## Key Takeaways
-
-1. Requires `PROJECT_ID` and `FINETUNED_MODEL_ALIAS` from a previous workflow. `DATASET_ID` is auto-fetched.
-2. Uses Claude 4.5 Sonnet to score responses (0-1 scale).
-3. Add ALL traces to the dataset - low-scoring traces are automatically improved.
-4. Each cycle adds training data focusing on model weaknesses.
diff --git a/api-reference/walkthroughs/jsonl-workflow.mdx b/api-reference/walkthroughs/jsonl-workflow.mdx
deleted file mode 100644
index e681b11b0b..0000000000
--- a/api-reference/walkthroughs/jsonl-workflow.mdx
+++ /dev/null
@@ -1,584 +0,0 @@
----
-title: "Fine-Tune from JSONL"
-description: "Upload a JSONL dataset, create snapshots, and launch fine-tuning jobs."
----
-
-
- Export your Prem API key as `API_KEY` before running any script.
-
-
-
-
-
- # Set JSONL file path
-
-
- ```ts TypeScript
- const API_KEY = process.env.API_KEY;
-
- // Define your JSONL dataset file
- const JSONL_FILE_PATH = 'sample_data.jsonl';
- ```
- ```python Python
- import os
- import time
- import requests
-
- API_KEY = os.getenv("API_KEY")
-
- # Define your JSONL dataset file
- JSONL_FILE_PATH = "sample_data.jsonl"
- ```
-
-
-
-
-
- # Create project
-
- Create a new project workspace. See [Create Project](/api-reference/projects/post-projects-create) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/projects/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ name: 'Test Project', goal: 'Test finetuning' })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { project_id } = await res.json();
- ```
- ```python Python
- response = requests.post(
- "https://studio.premai.io/api/v1/public/projects/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"name": "Test Project", "goal": "Test finetuning"}
- )
- response.raise_for_status()
- project_id = response.json()["project_id"]
- ```
-
-
-
-
-
- # Upload JSONL file
-
- Upload a JSONL file to create a dataset. See [Create Dataset from JSONL](/api-reference/datasets/post-datasets-create-from-jsonl) for details.
-
-
- ```ts TypeScript
- const formData = new FormData();
- formData.append('project_id', project_id);
- formData.append('name', 'Test Dataset');
- const jsonlFile = file(JSONL_FILE_PATH);
- formData.append('file', jsonlFile, JSONL_FILE_PATH);
-
- const res = await fetch('https://studio.premai.io/api/v1/public/datasets/create-from-jsonl', {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` },
- body: formData
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { dataset_id } = await res.json();
- ```
- ```python Python
- with open(JSONL_FILE_PATH, 'rb') as f:
- files = {'file': (JSONL_FILE_PATH, f, 'application/json')}
- data = {'project_id': project_id, 'name': 'Test Dataset'}
- response = requests.post(
- "https://studio.premai.io/api/v1/public/datasets/create-from-jsonl",
- headers={"Authorization": f"Bearer {API_KEY}"},
- files=files,
- data=data
- )
- response.raise_for_status()
- dataset_id = response.json()["dataset_id"]
- ```
-
-
-
-
-
- # Wait for processing
-
- Poll the dataset status until uploading completes. See [Get Dataset](/api-reference/datasets/get-datasets-datasetid) for details.
-
-
- ```ts TypeScript
- let dataset;
- do {
- await sleep(2000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/datasets/${dataset_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- dataset = await res.json();
- } while (dataset.status === 'processing');
- ```
- ```python Python
- while True:
- time.sleep(2)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/datasets/{dataset_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- dataset = response.json()
- if dataset["status"] != "processing":
- break
- ```
-
-
-
-
-
- # Create snapshot
-
- Create a snapshot with train/validation split. See [Create Snapshot](/api-reference/snapshots/post-snapshots-create) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dataset_id, split_percentage: 80 })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { snapshot_id } = await res.json();
- ```
- ```python Python
- response = requests.post(
- "https://studio.premai.io/api/v1/public/snapshots/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"dataset_id": dataset_id, "split_percentage": 80}
- )
- response.raise_for_status()
- snapshot_id = response.json()["snapshot_id"]
- ```
-
-
-
-
-
- # Get recommendations
-
- Generate and retrieve model recommendations for your snapshot. See [Generate Recommendations](/api-reference/recommendations/post-recommendations-generate) and [Get Recommendations](/api-reference/recommendations/get-recommendations-snapshotid) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res2 = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- recs = await res2.json();
- } while (recs.status === 'processing');
- ```
- ```python Python
- response = requests.post(
- "https://studio.premai.io/api/v1/public/recommendations/generate",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id}
- )
- response.raise_for_status()
-
- while True:
- time.sleep(5)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/recommendations/{snapshot_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- recs = response.json()
- if recs["status"] != "processing":
- break
- ```
-
-
-
-
-
- # Start fine-tuning
-
- Launch a fine-tuning job with recommended experiments. See [Create Fine-Tuning Job](/api-reference/finetuning/post-finetuning-create) for details.
-
-
- ```ts TypeScript
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- const res = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id, name: 'Test Job', experiments })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { job_id } = await res.json();
- ```
- ```python Python
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/finetuning/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id, "name": "Test Job", "experiments": experiments}
- )
- response.raise_for_status()
- job_id = response.json()["job_id"]
- ```
-
-
-
-
-
- # Monitor job
-
-
- ```ts TypeScript
- for (let i = 0; i < 30; i++) {
- await sleep(10000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/finetuning/${job_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const job = await res.json();
- console.log(`Status: ${job.status}`);
- job.experiments.forEach((e: any) => {
- console.log(` - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
- });
- if (job.status !== 'processing') break;
- }
- ```
- ```python Python
- for i in range(30):
- time.sleep(10)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/finetuning/{job_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- job = response.json()
- print(f"Status: {job['status']}")
- for exp in job["experiments"]:
- print(f" - Exp #{exp['experiment_number']}: {exp['status']} {exp.get('model_id', '')}")
- if job["status"] != "processing":
- break
- ```
-
-
- Monitor fine-tuning job progress and status. See [Get Fine-Tuning Job](/api-reference/finetuning/get-finetuning-jobid) for details.
-
-
-
-## Full Example
-
-
-```ts TypeScript
-#!/usr/bin/env bun
-
-/**
- * Example 1: JSONL dataset workflow
- * 1. Create project ā 2. Upload JSONL ā 3. Create snapshot ā 4. Get recommendations ā 5. Run finetuning
- */
-
-import { file } from 'bun';
-
-const API_KEY = process.env.API_KEY;
-const JSONL_FILE_PATH = 'sample_data.jsonl';
-
-if (!API_KEY) {
- console.error('Error: API_KEY environment variable is required');
- console.error('Please create a .env file based on .env.example');
- process.exit(1);
-}
-
-function sleep(ms: number) {
- return new Promise((r) => setTimeout(r, ms));
-}
-
-async function main() {
- console.log('\n=== JSONL Workflow ===\n');
-
- // 1. Create project
- console.log('1. Creating project...');
- const res1 = await fetch('https://studio.premai.io/api/v1/public/projects/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ name: 'Test Project', goal: 'Test finetuning' }),
- });
- if (!res1.ok) throw new Error(`${res1.status}: ${await res1.text()}`);
- const { project_id } = await res1.json();
- console.log(` ā Project: ${project_id}\n`);
-
- // 2. Upload JSONL
- console.log('2. Uploading JSONL dataset...');
- const formData = new FormData();
- formData.append('project_id', project_id);
- formData.append('name', 'Test Dataset');
- const jsonlFile = file(JSONL_FILE_PATH);
- formData.append('file', jsonlFile, JSONL_FILE_PATH);
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/datasets/create-from-jsonl', {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` },
- body: formData,
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- const { dataset_id } = await res2.json();
- console.log(` ā Dataset: ${dataset_id}`);
-
- // Wait for dataset
- console.log(' Waiting for dataset...');
- let dataset;
- do {
- await sleep(2000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/datasets/${dataset_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- dataset = await res.json();
- } while (dataset.status === 'processing');
- console.log(` ā Ready: ${dataset.datapoints_count} datapoints\n`);
-
- // 3. Create snapshot
- console.log('3. Creating snapshot...');
- const res3 = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dataset_id, split_percentage: 80 }),
- });
- if (!res3.ok) throw new Error(`${res3.status}: ${await res3.text()}`);
- const { snapshot_id } = await res3.json();
- console.log(` ā Snapshot: ${snapshot_id}\n`);
-
- // 4. Generate recommendations
- console.log('4. Generating recommendations...');
- const res4 = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id }),
- });
- if (!res4.ok) throw new Error(`${res4.status}: ${await res4.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- recs = await res.json();
- } while (recs.status === 'processing');
-
- console.log(` ā Recommended experiments:`);
- const recommendedCount = recs.recommended_experiments.filter((e: any) => e.recommended).length;
- console.log(` Total experiments: ${recs.recommended_experiments.length}, Recommended: ${recommendedCount}`);
- recs.recommended_experiments.forEach((e: any) => {
- if (e.recommended) console.log(` - ${e.base_model_id} (LoRA: ${e.lora})`);
- });
- console.log();
-
- // 5. Create finetuning job
- console.log('5. Creating finetuning job...');
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- if (experiments.length === 0) {
- console.error('\nā Error: No recommended experiments found. Cannot create finetuning job.');
- process.exit(1);
- }
-
- const res5 = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id, name: 'Test Job', experiments }),
- });
- if (!res5.ok) throw new Error(`${res5.status}: ${await res5.text()}`);
- const { job_id } = await res5.json();
- console.log(` ā Job: ${job_id}\n`);
-
- // 6. Monitor (5 minutes max)
- console.log('6. Monitoring job...');
- for (let i = 0; i < 30; i++) {
- await sleep(10000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/finetuning/${job_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const job = await res.json();
- console.log(` Status: ${job.status}`);
- job.experiments.forEach((e: any) => {
- console.log(` - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
- });
- if (job.status !== 'processing') break;
- }
-
- console.log('\nā Done!\n');
-}
-
-main().catch((err) => {
- console.error('\nā Error:', err.message);
- process.exit(1);
-});
-```
-```python Python
-#!/usr/bin/env python3
-
-"""
-Example 1: JSONL dataset workflow
-1. Create project ā 2. Upload JSONL ā 3. Create snapshot ā 4. Get recommendations ā 5. Run finetuning
-"""
-
-import os
-import time
-import requests
-
-API_KEY = os.getenv("API_KEY")
-JSONL_FILE_PATH = "sample_data.jsonl"
-
-if not API_KEY:
- print("Error: API_KEY environment variable is required")
- exit(1)
-
-
-def api(endpoint: str, method: str = "GET", **kwargs):
- response = requests.request(
- method=method,
- url=f"https://studio.premai.io{endpoint}",
- headers={"Authorization": f"Bearer {API_KEY}", **kwargs.pop("headers", {})},
- **kwargs
- )
- if not response.ok:
- err = response.json() if response.content else {}
- error_msg = err.get("error", str(err)) if isinstance(err, dict) else str(err)
- raise Exception(f"{response.status_code}: {error_msg}")
- return response.json()
-
-
-def main():
- print("\n=== JSONL Workflow ===\n")
-
- # Create project
- print("1. Creating project...")
- result = api("/api/v1/public/projects/create", method="POST", headers={"Content-Type": "application/json"}, json={"name": "Test Project", "goal": "Test finetuning"})
- project_id = result["project_id"]
- print(f" ā Project: {project_id}\n")
-
- # Upload JSONL
- print("2. Uploading JSONL dataset...")
- with open(JSONL_FILE_PATH, "rb") as f:
- files = {"file": (JSONL_FILE_PATH, f, "application/json")}
- data = {"project_id": project_id, "name": "Test Dataset"}
- result = api("/api/v1/public/datasets/create-from-jsonl", method="POST", files=files, data=data)
- dataset_id = result["dataset_id"]
- print(f" ā Dataset: {dataset_id}")
-
- # Wait for dataset
- print(" Waiting for dataset...")
- while True:
- time.sleep(2)
- dataset = api(f"/api/v1/public/datasets/{dataset_id}")
- if dataset["status"] != "processing":
- break
- print(f" ā Ready: {dataset['datapoints_count']} datapoints\n")
-
- # Create snapshot
- print("3. Creating snapshot...")
- result = api("/api/v1/public/snapshots/create", method="POST", headers={"Content-Type": "application/json"}, json={"dataset_id": dataset_id, "split_percentage": 80})
- snapshot_id = result["snapshot_id"]
- print(f" ā Snapshot: {snapshot_id}\n")
-
- # Generate recommendations
- print("4. Generating recommendations...")
- api("/api/v1/public/recommendations/generate", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id})
-
- while True:
- time.sleep(5)
- recs = api(f"/api/v1/public/recommendations/{snapshot_id}")
- if recs["status"] != "processing":
- break
-
- print(" ā Recommended experiments:")
- recommended_count = sum(1 for e in recs["recommended_experiments"] if e["recommended"])
- print(f" Total experiments: {len(recs['recommended_experiments'])}, Recommended: {recommended_count}")
- for e in recs["recommended_experiments"]:
- if e["recommended"]:
- print(f" - {e['base_model_id']} (LoRA: {e['lora']})")
- print()
-
- # Create finetuning job
- print("5. Creating finetuning job...")
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- if not experiments:
- print("\nā Error: No recommended experiments found. Cannot create finetuning job.")
- exit(1)
-
- result = api("/api/v1/public/finetuning/create", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id, "name": "Test Job", "experiments": experiments})
- job_id = result["job_id"]
- print(f" ā Job: {job_id}\n")
-
- # Monitor (5 minutes max)
- print("6. Monitoring job...")
- for i in range(30):
- time.sleep(10)
- job = api(f"/api/v1/public/finetuning/{job_id}")
- print(f" Status: {job['status']}")
- for exp in job["experiments"]:
- print(f" - Exp #{exp['experiment_number']}: {exp['status']} {exp.get('model_id', '')}")
- if job["status"] != "processing":
- break
-
- print("\nā Done!\n")
-
-
-if __name__ == "__main__":
- try:
- main()
- except Exception as err:
- print(f"\nā Error: {err}")
- exit(1)
-```
-
diff --git a/api-reference/walkthroughs/pdf-synthetic-workflow.mdx b/api-reference/walkthroughs/pdf-synthetic-workflow.mdx
deleted file mode 100644
index 70709eb2dc..0000000000
--- a/api-reference/walkthroughs/pdf-synthetic-workflow.mdx
+++ /dev/null
@@ -1,782 +0,0 @@
----
-title: "Fine-Tune with PDF Synthetic Dataset"
-description: "Generate synthetic Q&A pairs from PDF documents, create snapshots, and launch fine-tuning."
----
-
-## Overview
-
-Build a structured data extraction model from PDFs:
-- Upload PDFs to generate synthetic training data
-- Use question/answer templates to enforce JSON output format
-- Extract fields like date, amount, currency, business name, and location
-- Create snapshots, get recommendations, and launch fine-tuning
-
-
- Export your Prem API key as `API_KEY` before running any script.
- Place your invoice PDF files in the same directory or update the `PDF_FILES` array.
-
-
-
-
-
- # Set PDF file paths
-
-
- ```ts TypeScript
- const API_KEY = process.env.API_KEY;
-
- // Define your invoice PDF files to process (50 files, one QA pair per file)
- const PDF_FILES: string[] = [];
- for (let i = 1; i <= 50; i++) {
- PDF_FILES.push(`invoice_${i}.pdf`);
- }
- ```
- ```python Python
- import os
- import requests
-
- API_KEY = os.getenv("API_KEY")
-
- # Define your invoice PDF files to process (50 files, one QA pair per file)
- PDF_FILES = [f"invoice_{i}.pdf" for i in range(1, 51)]
- ```
-
-
- Define the PDF files you want to process. Make sure these files exist in your working directory.
-
-
-
- # Generate dataset from PDFs
-
- Create a project and generate synthetic Q&A pairs from PDF files. See [Create Project](/api-reference/projects/post-projects-create) and [Create Synthetic Dataset](/api-reference/datasets/post-datasets-create-synthetic) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/projects/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ name: 'Invoice Extraction Project', goal: 'Extract structured data from receipts' })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { project_id } = await res.json();
-
- const formData = new FormData();
- formData.append('project_id', project_id);
- formData.append('name', 'Invoice Receipts Dataset');
-
- // Upload multiple PDF files
- PDF_FILES.forEach((pdfPath: string) => {
- const pdfFile = file(pdfPath);
- formData.append('files[]', pdfFile, pdfPath);
- });
-
- formData.append('pairs_to_generate', String(PDF_FILES.length));
- formData.append('pair_type', 'qa');
- formData.append('temperature', '0');
-
- // Add rules and constraints
- formData.append('rules[]', 'Always include the full extracted text in the question');
- formData.append('rules[]', 'Clearly instruct extraction into the given JSON schema');
- formData.append('rules[]', 'Only output the JSON object (no extra text)');
- formData.append('rules[]', 'Fill fields only if explicitly present in the text');
- formData.append('rules[]', 'If a field is missing, leave it empty but keep the key');
- formData.append('rules[]', 'Strictly follow the schema');
- formData.append('rules[]', 'Never infer, guess, or fabricate information');
-
- // Define question format
- const questionFormat = `{EXTRACTED_TEXT}
-
-Task: Extract all the information available in the text and present it in the JSON format below.
-Do not infer or invent details ā only include what is explicitly stated.
-
-JSON Schema:
-{
- "DateTime": "YYYY-MM-DD HH:MM:SS",
- "Total Amount": "number",
- "Currency": "string",
- "Business Name": "string",
- "Business Location": "string"
-}`;
- formData.append('question_format', questionFormat);
-
- // Define answer format
- const answerFormat = `{
- "DateTime": "",
- "Total Amount": "",
- "Currency": "",
- "Business Name": "",
- "Business Location": ""
-}`;
- formData.append('answer_format', answerFormat);
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/datasets/create-synthetic', {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` },
- body: formData
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- const { dataset_id } = await res2.json();
- ```
- ```python Python
- response = requests.post(
- "https://studio.premai.io/api/v1/public/projects/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"name": "Invoice Extraction Project", "goal": "Extract structured data from receipts"}
- )
- response.raise_for_status()
- project_id = response.json()["project_id"]
-
- # Prepare files to upload
- files = [(file_path, open(file_path, 'rb'), 'application/pdf') for file_path in PDF_FILES]
- files_dict = [('files[]', f) for f in files]
-
- # Define question format
- question_format = """{EXTRACTED_TEXT}
-
-Task: Extract all the information available in the text and present it in the JSON format below.
-Do not infer or invent details ā only include what is explicitly stated.
-
-JSON Schema:
-{
- "DateTime": "YYYY-MM-DD HH:MM:SS",
- "Total Amount": "number",
- "Currency": "string",
- "Business Name": "string",
- "Business Location": "string"
-}"""
-
- # Define answer format
- answer_format = """{
- "DateTime": "",
- "Total Amount": "",
- "Currency": "",
- "Business Name": "",
- "Business Location": ""
-}"""
-
- data = {
- 'project_id': project_id,
- 'name': 'Invoice Receipts Dataset',
- 'pairs_to_generate': str(len(PDF_FILES)),
- 'pair_type': 'qa',
- 'temperature': '0',
- 'rules[]': [
- 'Always include the full extracted text in the question',
- 'Clearly instruct extraction into the given JSON schema',
- 'Only output the JSON object (no extra text)',
- 'Fill fields only if explicitly present in the text',
- 'If a field is missing, leave it empty but keep the key',
- 'Strictly follow the schema',
- 'Never infer, guess, or fabricate information'
- ],
- 'question_format': question_format,
- 'answer_format': answer_format
- }
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/datasets/create-synthetic",
- headers={"Authorization": f"Bearer {API_KEY}"},
- files=files_dict,
- data=data
- )
-
- # Close file handles
- for _, f, _ in files:
- f.close()
-
- response.raise_for_status()
- dataset_id = response.json()["dataset_id"]
- ```
-
-
-
-
-
- # Wait for generation
-
- Poll the dataset status until generation completes. See [Get Dataset](/api-reference/datasets/get-datasets-datasetid) for details.
-
-
- ```ts TypeScript
- let dataset;
- let checks = 0;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/datasets/${dataset_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- dataset = await res.json();
- if (checks++ % 6 === 0) {
- console.log(`Status: ${dataset.status}, ${dataset.datapoints_count} datapoints`);
- }
- } while (dataset.status === 'processing');
- ```
- ```python Python
- checks = 0
- while True:
- time.sleep(5)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/datasets/{dataset_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- dataset = response.json()
- if checks % 6 == 0:
- print(f"Status: {dataset['status']}, {dataset['datapoints_count']} datapoints")
- checks += 1
- if dataset["status"] != "processing":
- break
- ```
-
-
-
-
-
- # Create snapshot and get recommendations
-
- Create a snapshot and generate model recommendations. See [Create Snapshot](/api-reference/snapshots/post-snapshots-create), [Generate Recommendations](/api-reference/recommendations/post-recommendations-generate), and [Get Recommendations](/api-reference/recommendations/get-recommendations-snapshotid) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dataset_id, split_percentage: 80 })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { snapshot_id } = await res.json();
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id })
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res3 = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res3.ok) throw new Error(`${res3.status}: ${await res3.text()}`);
- recs = await res3.json();
- } while (recs.status === 'processing');
- ```
- ```python Python
- response = requests.post(
- "https://studio.premai.io/api/v1/public/snapshots/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"dataset_id": dataset_id, "split_percentage": 80}
- )
- response.raise_for_status()
- snapshot_id = response.json()["snapshot_id"]
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/recommendations/generate",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id}
- )
- response.raise_for_status()
-
- while True:
- time.sleep(5)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/recommendations/{snapshot_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- recs = response.json()
- if recs["status"] != "processing":
- break
- ```
-
-
-
-
-
- # Start fine-tuning
-
- Launch a fine-tuning job with recommended experiments. See [Create Fine-Tuning Job](/api-reference/finetuning/post-finetuning-create) for details.
-
-
- ```ts TypeScript
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- const res = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id, name: 'PepsiCo By-Laws Model', experiments })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { job_id } = await res.json();
- ```
- ```python Python
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/finetuning/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id, "name": "PepsiCo By-Laws Model", "experiments": experiments}
- )
- response.raise_for_status()
- job_id = response.json()["job_id"]
- ```
-
-
-
-
-
- # Monitor job
-
-
- ```ts TypeScript
- for (let i = 0; i < 30; i++) {
- await sleep(10000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/finetuning/${job_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const job = await res.json();
- console.log(`Status: ${job.status}`);
- job.experiments.forEach((e: any) => {
- console.log(` - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
- });
- if (job.status !== 'processing') break;
- }
- ```
- ```python Python
- for i in range(30):
- time.sleep(10)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/finetuning/{job_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- job = response.json()
- print(f"Status: {job['status']}")
- for exp in job["experiments"]:
- print(f" - Exp #{exp['experiment_number']}: {exp['status']} {exp.get('model_id', '')}")
- if job["status"] != "processing":
- break
- ```
-
-
- Monitor fine-tuning job progress and status. See [Get Fine-Tuning Job](/api-reference/finetuning/get-finetuning-jobid) for details.
-
-
-
-## Full Example
-
-
-```ts TypeScript
-#!/usr/bin/env bun
-
-/**
- * Example 3: PDF synthetic dataset workflow
- * 1. Create project ā 2. Generate synthetic data from PDF ā 3. Create snapshot ā 4. Get recommendations ā 5. Run finetuning
- */
-
-import { file } from 'bun';
-
-const API_KEY = process.env.API_KEY;
-
-// Define your invoice PDF files to process (50 files, one QA pair per file)
-const PDF_FILES: string[] = [];
-for (let i = 1; i <= 50; i++) {
- PDF_FILES.push(`invoice_${i}.pdf`);
-}
-
-if (!API_KEY) {
- console.error('Error: API_KEY environment variable is required');
- console.error('Please create a .env file based on .env.example');
- process.exit(1);
-}
-
-function sleep(ms: number) {
- return new Promise((r) => setTimeout(r, ms));
-}
-
-async function main() {
- console.log('\n=== PDF Synthetic Workflow ===\n');
-
- // 1. Create project
- console.log('1. Creating project...');
- const res1 = await fetch('https://studio.premai.io/api/v1/public/projects/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ name: 'Invoice Extraction Project', goal: 'Extract structured data from receipts' }),
- });
- if (!res1.ok) throw new Error(`${res1.status}: ${await res1.text()}`);
- const { project_id } = await res1.json();
- console.log(` ā Project: ${project_id}\n`);
-
- // 2. Generate synthetic dataset from PDF
- console.log('2. Generating synthetic dataset from PDFs...');
- console.log(` Files: ${PDF_FILES.join(', ')}`);
- const formData = new FormData();
- formData.append('project_id', project_id);
- formData.append('name', 'Invoice Receipts Dataset');
-
- // Upload multiple PDF files
- PDF_FILES.forEach((pdfPath) => {
- const pdfFile = file(pdfPath);
- formData.append('files[]', pdfFile, pdfPath);
- });
-
- formData.append('pairs_to_generate', String(PDF_FILES.length));
- formData.append('pair_type', 'qa');
- formData.append('temperature', '0');
-
- // Add rules and constraints
- formData.append('rules[]', 'Always include the full extracted text in the question');
- formData.append('rules[]', 'Clearly instruct extraction into the given JSON schema');
- formData.append('rules[]', 'Only output the JSON object (no extra text)');
- formData.append('rules[]', 'Fill fields only if explicitly present in the text');
- formData.append('rules[]', 'If a field is missing, leave it empty but keep the key');
- formData.append('rules[]', 'Strictly follow the schema');
- formData.append('rules[]', 'Never infer, guess, or fabricate information');
-
- // Define question format
- const questionFormat = `{EXTRACTED_TEXT}
-
-Task: Extract all the information available in the text and present it in the JSON format below.
-Do not infer or invent details ā only include what is explicitly stated.
-
-JSON Schema:
-{
- "DateTime": "YYYY-MM-DD HH:MM:SS",
- "Total Amount": "number",
- "Currency": "string",
- "Business Name": "string",
- "Business Location": "string"
-}`;
- formData.append('question_format', questionFormat);
-
- // Define answer format
- const answerFormat = `{
- "DateTime": "",
- "Total Amount": "",
- "Currency": "",
- "Business Name": "",
- "Business Location": ""
-}`;
- formData.append('answer_format', answerFormat);
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/datasets/create-synthetic', {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` },
- body: formData,
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- const { dataset_id } = await res2.json();
- console.log(` ā Dataset: ${dataset_id}`);
-
- // Wait for dataset (can take several minutes)
- console.log(' Waiting for generation (may take 5-10 minutes)...');
- let dataset;
- let checks = 0;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/datasets/${dataset_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- dataset = await res.json();
- if (checks++ % 6 === 0) {
- console.log(` Status: ${dataset.status}, ${dataset.datapoints_count} datapoints`);
- }
- } while (dataset.status === 'processing');
- console.log(` ā Ready: ${dataset.datapoints_count} datapoints\n`);
-
- // 3. Create snapshot
- console.log('3. Creating snapshot...');
- const res3 = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dataset_id, split_percentage: 80 }),
- });
- if (!res3.ok) throw new Error(`${res3.status}: ${await res3.text()}`);
- const { snapshot_id } = await res3.json();
- console.log(` ā Snapshot: ${snapshot_id}\n`);
-
- // 4. Generate recommendations
- console.log('4. Generating recommendations...');
- const res4 = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id }),
- });
- if (!res4.ok) throw new Error(`${res4.status}: ${await res4.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- recs = await res.json();
- } while (recs.status === 'processing');
-
- console.log(` ā Recommended experiments:`);
- const recommendedCount = recs.recommended_experiments.filter((e: any) => e.recommended).length;
- console.log(` Total experiments: ${recs.recommended_experiments.length}, Recommended: ${recommendedCount}`);
- recs.recommended_experiments.forEach((e: any) => {
- if (e.recommended) console.log(` - ${e.base_model_id} (LoRA: ${e.lora})`);
- });
- console.log();
-
- // 5. Create finetuning job
- console.log('5. Creating finetuning job...');
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- if (experiments.length === 0) {
- console.error('\nā Error: No recommended experiments found. Cannot create finetuning job.');
- process.exit(1);
- }
-
- const res5 = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id, name: 'PepsiCo By-Laws Model', experiments }),
- });
- if (!res5.ok) throw new Error(`${res5.status}: ${await res5.text()}`);
- const { job_id } = await res5.json();
- console.log(` ā Job: ${job_id}\n`);
-
- // 6. Monitor (5 minutes max)
- console.log('6. Monitoring job...');
- for (let i = 0; i < 30; i++) {
- await sleep(10000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/finetuning/${job_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const job = await res.json();
- console.log(` Status: ${job.status}`);
- job.experiments.forEach((e: any) => {
- console.log(` - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
- });
- if (job.status !== 'processing') break;
- }
-
- console.log('\nā Done!\n');
-}
-
-main().catch((err) => {
- console.error('\nā Error:', err.message);
- process.exit(1);
-});
-```
-```python Python
-#!/usr/bin/env python3
-
-"""
-Example 3: PDF synthetic dataset workflow
-1. Create project ā 2. Generate synthetic data from PDF ā 3. Create snapshot ā 4. Get recommendations ā 5. Run finetuning
-"""
-
-import os
-import time
-import requests
-
-API_KEY = os.getenv("API_KEY")
-
-# Define your invoice PDF files to process (50 files, one QA pair per file)
-PDF_FILES = [f"invoice_{i}.pdf" for i in range(1, 51)]
-
-if not API_KEY:
- print("Error: API_KEY environment variable is required")
- exit(1)
-
-
-def api(endpoint: str, method: str = "GET", **kwargs):
- response = requests.request(
- method=method,
- url=f"https://studio.premai.io{endpoint}",
- headers={"Authorization": f"Bearer {API_KEY}", **kwargs.pop("headers", {})},
- **kwargs
- )
- if not response.ok:
- err = response.json() if response.content else {}
- error_msg = err.get("error", str(err)) if isinstance(err, dict) else str(err)
- raise Exception(f"{response.status_code}: {error_msg}")
- return response.json()
-
-
-def main():
- print("\n=== PDF Synthetic Workflow ===\n")
-
- # Create project
- print("1. Creating project...")
- result = api("/api/v1/public/projects/create", method="POST", headers={"Content-Type": "application/json"}, json={"name": "Invoice Extraction Project", "goal": "Extract structured data from receipts"})
- project_id = result["project_id"]
- print(f" ā Project: {project_id}\n")
-
- # Generate synthetic dataset from PDF
- print("2. Generating synthetic dataset from PDFs...")
- print(f" Files: {', '.join(PDF_FILES)}")
-
- # Prepare files to upload
- files = [(file_path, open(file_path, 'rb'), 'application/pdf') for file_path in PDF_FILES]
- files_dict = [('files[]', f) for f in files]
-
- # Define question format
- question_format = """{EXTRACTED_TEXT}
-
-Task: Extract all the information available in the text and present it in the JSON format below.
-Do not infer or invent details ā only include what is explicitly stated.
-
-JSON Schema:
-{
- "DateTime": "YYYY-MM-DD HH:MM:SS",
- "Total Amount": "number",
- "Currency": "string",
- "Business Name": "string",
- "Business Location": "string"
-}"""
-
- # Define answer format
- answer_format = """{
- "DateTime": "",
- "Total Amount": "",
- "Currency": "",
- "Business Name": "",
- "Business Location": ""
-}"""
-
- data = {
- "project_id": project_id,
- "name": "Invoice Receipts Dataset",
- "pairs_to_generate": str(len(PDF_FILES)),
- "pair_type": "qa",
- "temperature": "0",
- "rules[]": [
- "Always include the full extracted text in the question",
- "Clearly instruct extraction into the given JSON schema",
- "Only output the JSON object (no extra text)",
- "Fill fields only if explicitly present in the text",
- "If a field is missing, leave it empty but keep the key",
- "Strictly follow the schema",
- "Never infer, guess, or fabricate information"
- ],
- "question_format": question_format,
- "answer_format": answer_format
- }
-
- result = api("/api/v1/public/datasets/create-synthetic", method="POST", files=files_dict, data=data)
-
- # Close file handles
- for _, f, _ in files:
- f.close()
-
- dataset_id = result["dataset_id"]
- print(f" ā Dataset: {dataset_id}")
-
- # Wait for dataset (can take several minutes)
- print(" Waiting for generation (may take 5-10 minutes)...")
- checks = 0
- while True:
- time.sleep(5)
- dataset = api(f"/api/v1/public/datasets/{dataset_id}")
- if checks % 6 == 0:
- print(f" Status: {dataset['status']}, {dataset['datapoints_count']} datapoints")
- checks += 1
- if dataset["status"] != "processing":
- break
- print(f" ā Ready: {dataset['datapoints_count']} datapoints\n")
-
- # Create snapshot
- print("3. Creating snapshot...")
- result = api("/api/v1/public/snapshots/create", method="POST", headers={"Content-Type": "application/json"}, json={"dataset_id": dataset_id, "split_percentage": 80})
- snapshot_id = result["snapshot_id"]
- print(f" ā Snapshot: {snapshot_id}\n")
-
- # Generate recommendations
- print("4. Generating recommendations...")
- api("/api/v1/public/recommendations/generate", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id})
-
- while True:
- time.sleep(5)
- recs = api(f"/api/v1/public/recommendations/{snapshot_id}")
- if recs["status"] != "processing":
- break
-
- print(" ā Recommended experiments:")
- recommended_count = sum(1 for e in recs["recommended_experiments"] if e["recommended"])
- print(f" Total experiments: {len(recs['recommended_experiments'])}, Recommended: {recommended_count}")
- for e in recs["recommended_experiments"]:
- if e["recommended"]:
- print(f" - {e['base_model_id']} (LoRA: {e['lora']})")
- print()
-
- # Create finetuning job
- print("5. Creating finetuning job...")
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- if not experiments:
- print("\nā Error: No recommended experiments found. Cannot create finetuning job.")
- exit(1)
-
- result = api("/api/v1/public/finetuning/create", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id, "name": "PepsiCo By-Laws Model", "experiments": experiments})
- job_id = result["job_id"]
- print(f" ā Job: {job_id}\n")
-
- # Monitor (5 minutes max)
- print("6. Monitoring job...")
- for i in range(30):
- time.sleep(10)
- job = api(f"/api/v1/public/finetuning/{job_id}")
- print(f" Status: {job['status']}")
- for exp in job["experiments"]:
- print(f" - Exp #{exp['experiment_number']}: {exp['status']} {exp.get('model_id', '')}")
- if job["status"] != "processing":
- break
-
- print("\nā Done!\n")
-
-
-if __name__ == "__main__":
- try:
- main()
- except Exception as err:
- print(f"\nā Error: {err}")
- exit(1)
-```
-
diff --git a/api-reference/walkthroughs/youtube-synthetic-workflow.mdx b/api-reference/walkthroughs/youtube-synthetic-workflow.mdx
deleted file mode 100644
index f4da1b0a4d..0000000000
--- a/api-reference/walkthroughs/youtube-synthetic-workflow.mdx
+++ /dev/null
@@ -1,781 +0,0 @@
----
-title: "Fine-Tune with YouTube Synthetic Dataset"
-description: "Generate synthetic Q&A pairs from YouTube videos, create snapshots, and launch fine-tuning."
----
-
-## Overview
-
-Build a model that extracts structured data from YouTube videos:
-- Process YouTube videos to generate synthetic training data
-- Use question/answer templates to extract JSON output
-- Extract stock tickers, investment tips, risks, and topics
-- Create snapshots, get recommendations, and launch fine-tuning
-
-
- Export your Prem API key as `API_KEY` before running any script.
- The example uses 5 curated financial YouTube videos. You can modify the `YOUTUBE_URLS` array to use your own videos.
-
-
-
-
-
- # Set YouTube URLs
-
-
- ```ts TypeScript
- const API_KEY = process.env.API_KEY;
-
- // Define the YouTube videos you want to analyze
- const YOUTUBE_URLS = [
- 'https://www.youtube.com/watch?v=JH-k5f4Yclc',
- 'https://www.youtube.com/watch?v=YEWhxcpMS1c',
- 'https://www.youtube.com/watch?v=cb8up3HVXis',
- 'https://www.youtube.com/watch?v=26xatIiMv88',
- 'https://www.youtube.com/watch?v=-Da3gUdzCvs'
- ];
- ```
- ```python Python
- import os
- import requests
-
- API_KEY = os.getenv("API_KEY")
-
- # Define the YouTube videos you want to analyze
- YOUTUBE_URLS = [
- "https://www.youtube.com/watch?v=JH-k5f4Yclc",
- "https://www.youtube.com/watch?v=YEWhxcpMS1c",
- "https://www.youtube.com/watch?v=cb8up3HVXis",
- "https://www.youtube.com/watch?v=26xatIiMv88",
- "https://www.youtube.com/watch?v=-Da3gUdzCvs"
- ]
- ```
-
-
-
-
-
- # Generate dataset from YouTube
-
- Create a project and generate synthetic Q&A pairs from YouTube videos. See [Create Project](/api-reference/projects/post-projects-create) and [Create Synthetic Dataset](/api-reference/datasets/post-datasets-create-synthetic) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/projects/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ name: 'Stock Analysis Project', goal: 'Extract investment insights from financial videos' })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { project_id } = await res.json();
-
- const formData = new FormData();
- formData.append('project_id', project_id);
- formData.append('name', 'Financial YouTube Dataset');
-
- // Add YouTube URLs
- YOUTUBE_URLS.forEach((url: string, index: number) => {
- formData.append(`youtube_urls[${index}]`, url);
- });
-
- formData.append('pairs_to_generate', '50');
- formData.append('pair_type', 'qa');
- formData.append('temperature', '0.3');
-
- // Add rules and constraints
- formData.append('rules[]', 'stocks_mentioned: List all stock ticker symbols mentioned (e.g., AAPL, TSLA, NVDA)');
- formData.append('rules[]', 'investment_tips: Extract 3-5 specific, actionable pieces of advice from the video');
- formData.append('rules[]', 'risks_mentioned: List any warnings, risks, or cautionary statements discussed');
- formData.append('rules[]', 'video_topic: Write a short phrase describing the main topic of the video');
- formData.append('rules[]', 'Only output valid JSON with no additional text before or after');
- formData.append('rules[]', 'If a field has no relevant information, use an empty array [] or empty string ""');
- formData.append('rules[]', 'Use exact quotes or close paraphrases from the video content');
- formData.append('rules[]', 'Do not invent or infer information not explicitly stated');
-
- // Define question format
- const questionFormat = `Extract investment information from the following video transcript:
-
-{VIDEO_TRANSCRIPT}
-
-Provide the output in this JSON format:
-{
- "stocks_mentioned": ["TICKER1", "TICKER2"],
- "investment_tips": ["tip 1", "tip 2", "tip 3"],
- "risks_mentioned": ["risk 1", "risk 2"],
- "video_topic": "brief description of main topic"
-}`;
- formData.append('question_format', questionFormat);
-
- // Define answer format
- const answerFormat = `{
- "stocks_mentioned": ["", ""],
- "investment_tips": ["", "", ""],
- "risks_mentioned": ["", ""],
- "video_topic": ""
-}`;
- formData.append('answer_format', answerFormat);
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/datasets/create-synthetic', {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` },
- body: formData
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- const { dataset_id } = await res2.json();
- ```
- ```python Python
- YOUTUBE_URLS = [
- "https://www.youtube.com/watch?v=JH-k5f4Yclc",
- "https://www.youtube.com/watch?v=YEWhxcpMS1c",
- "https://www.youtube.com/watch?v=cb8up3HVXis",
- "https://www.youtube.com/watch?v=26xatIiMv88",
- "https://www.youtube.com/watch?v=-Da3gUdzCvs"
- ]
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/projects/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"name": "Stock Analysis Project", "goal": "Extract investment insights from financial videos"}
- )
- response.raise_for_status()
- project_id = response.json()["project_id"]
-
- # Define question format
- question_format = """Extract investment information from the following video transcript:
-
-{VIDEO_TRANSCRIPT}
-
-Provide the output in this JSON format:
-{
- "stocks_mentioned": ["TICKER1", "TICKER2"],
- "investment_tips": ["tip 1", "tip 2", "tip 3"],
- "risks_mentioned": ["risk 1", "risk 2"],
- "video_topic": "brief description of main topic"
-}"""
-
- # Define answer format
- answer_format = """{
- "stocks_mentioned": ["", ""],
- "investment_tips": ["", "", ""],
- "risks_mentioned": ["", ""],
- "video_topic": ""
-}"""
-
- form_data = {
- "project_id": project_id,
- "name": "Financial YouTube Dataset",
- "pairs_to_generate": "50",
- "pair_type": "qa",
- "temperature": "0.3",
- "rules[]": [
- "stocks_mentioned: List all stock ticker symbols mentioned (e.g., AAPL, TSLA, NVDA)",
- "investment_tips: Extract 3-5 specific, actionable pieces of advice from the video",
- "risks_mentioned: List any warnings, risks, or cautionary statements discussed",
- "video_topic: Write a short phrase describing the main topic of the video",
- "Only output valid JSON with no additional text before or after",
- "If a field has no relevant information, use an empty array [] or empty string \"\"",
- "Use exact quotes or close paraphrases from the video content",
- "Do not invent or infer information not explicitly stated"
- ],
- "question_format": question_format,
- "answer_format": answer_format
- }
-
- # Add YouTube URLs
- for i, url in enumerate(YOUTUBE_URLS):
- form_data[f"youtube_urls[{i}]"] = url
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/datasets/create-synthetic",
- headers={"Authorization": f"Bearer {API_KEY}"},
- data=form_data
- )
- response.raise_for_status()
- dataset_id = response.json()["dataset_id"]
- ```
-
-
- Generation can take 5-10 minutes.
-
-
-
- # Wait for generation
-
- Poll the dataset status until generation completes. See [Get Dataset](/api-reference/datasets/get-datasets-datasetid) for details.
-
-
- ```ts TypeScript
- let dataset;
- let checks = 0;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/datasets/${dataset_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- dataset = await res.json();
- if (checks++ % 6 === 0) {
- console.log(`Status: ${dataset.status}, ${dataset.datapoints_count} datapoints`);
- }
- } while (dataset.status === 'processing');
- ```
- ```python Python
- checks = 0
- while True:
- time.sleep(5)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/datasets/{dataset_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- dataset = response.json()
- if checks % 6 == 0:
- print(f"Status: {dataset['status']}, {dataset['datapoints_count']} datapoints")
- checks += 1
- if dataset["status"] != "processing":
- break
- ```
-
-
-
-
-
- # Create snapshot and get recommendations
-
- Create a snapshot and generate model recommendations. See [Create Snapshot](/api-reference/snapshots/post-snapshots-create), [Generate Recommendations](/api-reference/recommendations/post-recommendations-generate), and [Get Recommendations](/api-reference/recommendations/get-recommendations-snapshotid) for details.
-
-
- ```ts TypeScript
- const res = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dataset_id, split_percentage: 80 })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { snapshot_id } = await res.json();
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id })
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res3 = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res3.ok) throw new Error(`${res3.status}: ${await res3.text()}`);
- recs = await res3.json();
- } while (recs.status === 'processing');
- ```
- ```python Python
- response = requests.post(
- "https://studio.premai.io/api/v1/public/snapshots/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"dataset_id": dataset_id, "split_percentage": 80}
- )
- response.raise_for_status()
- snapshot_id = response.json()["snapshot_id"]
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/recommendations/generate",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id}
- )
- response.raise_for_status()
-
- while True:
- time.sleep(5)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/recommendations/{snapshot_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- recs = response.json()
- if recs["status"] != "processing":
- break
- ```
-
-
-
-
-
- # Start fine-tuning
-
- Launch a fine-tuning job with recommended experiments. See [Create Fine-Tuning Job](/api-reference/finetuning/post-finetuning-create) for details.
-
-
- ```ts TypeScript
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- const res = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id, name: 'YouTube Model', experiments })
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const { job_id } = await res.json();
- ```
- ```python Python
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- response = requests.post(
- "https://studio.premai.io/api/v1/public/finetuning/create",
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
- json={"snapshot_id": snapshot_id, "name": "YouTube Model", "experiments": experiments}
- )
- response.raise_for_status()
- job_id = response.json()["job_id"]
- ```
-
-
-
-
-
- # Monitor job
-
-
- ```ts TypeScript
- for (let i = 0; i < 30; i++) {
- await sleep(10000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/finetuning/${job_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const job = await res.json();
- console.log(`Status: ${job.status}`);
- job.experiments.forEach((e: any) => {
- console.log(` - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
- });
- if (job.status !== 'processing') break;
- }
- ```
- ```python Python
- for i in range(30):
- time.sleep(10)
- response = requests.get(
- f"https://studio.premai.io/api/v1/public/finetuning/{job_id}",
- headers={"Authorization": f"Bearer {API_KEY}"}
- )
- response.raise_for_status()
- job = response.json()
- print(f"Status: {job['status']}")
- for exp in job["experiments"]:
- print(f" - Exp #{exp['experiment_number']}: {exp['status']} {exp.get('model_id', '')}")
- if job["status"] != "processing":
- break
- ```
-
-
- Monitor fine-tuning job progress and status. See [Get Fine-Tuning Job](/api-reference/finetuning/get-finetuning-jobid) for details.
-
-
-
-## Full Example
-
-
-```ts TypeScript
-#!/usr/bin/env bun
-
-/**
- * Example 2: YouTube synthetic dataset workflow
- * 1. Create project ā 2. Generate synthetic data from YouTube ā 3. Create snapshot ā 4. Get recommendations ā 5. Run finetuning
- */
-
-const API_KEY = process.env.API_KEY;
-const YOUTUBE_URLS = [
- 'https://www.youtube.com/watch?v=JH-k5f4Yclc',
- 'https://www.youtube.com/watch?v=YEWhxcpMS1c',
- 'https://www.youtube.com/watch?v=cb8up3HVXis',
- 'https://www.youtube.com/watch?v=26xatIiMv88',
- 'https://www.youtube.com/watch?v=-Da3gUdzCvs'
-];
-
-if (!API_KEY) {
- console.error('Error: API_KEY environment variable is required');
- console.error('Please create a .env file based on .env.example');
- process.exit(1);
-}
-
-function sleep(ms: number) {
- return new Promise((r) => setTimeout(r, ms));
-}
-
-async function main() {
- console.log('\n=== YouTube Synthetic Workflow ===\n');
-
- // 1. Create project
- console.log('1. Creating project...');
- const res1 = await fetch('https://studio.premai.io/api/v1/public/projects/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ name: 'Stock Analysis Project', goal: 'Extract investment insights from financial videos' }),
- });
- if (!res1.ok) throw new Error(`${res1.status}: ${await res1.text()}`);
- const { project_id } = await res1.json();
- console.log(` ā Project: ${project_id}\n`);
-
- // 2. Generate synthetic dataset
- console.log('2. Generating synthetic dataset from YouTube...');
- console.log(` URLs: ${YOUTUBE_URLS.length} financial videos`);
- const formData = new FormData();
- formData.append('project_id', project_id);
- formData.append('name', 'Financial YouTube Dataset');
-
- // Add multiple YouTube URLs
- YOUTUBE_URLS.forEach((url, index) => {
- formData.append(`youtube_urls[${index}]`, url);
- });
-
- formData.append('pairs_to_generate', '50');
- formData.append('pair_type', 'qa');
- formData.append('temperature', '0.3');
-
- // Add rules and constraints
- formData.append('rules[]', 'stocks_mentioned: List all stock ticker symbols mentioned (e.g., AAPL, TSLA, NVDA)');
- formData.append('rules[]', 'investment_tips: Extract 3-5 specific, actionable pieces of advice from the video');
- formData.append('rules[]', 'risks_mentioned: List any warnings, risks, or cautionary statements discussed');
- formData.append('rules[]', 'video_topic: Write a short phrase describing the main topic of the video');
- formData.append('rules[]', 'Only output valid JSON with no additional text before or after');
- formData.append('rules[]', 'If a field has no relevant information, use an empty array [] or empty string ""');
- formData.append('rules[]', 'Use exact quotes or close paraphrases from the video content');
- formData.append('rules[]', 'Do not invent or infer information not explicitly stated');
-
- // Define question format
- const questionFormat = `Extract investment information from the following video transcript:
-
-{VIDEO_TRANSCRIPT}
-
-Provide the output in this JSON format:
-{
- "stocks_mentioned": ["TICKER1", "TICKER2"],
- "investment_tips": ["tip 1", "tip 2", "tip 3"],
- "risks_mentioned": ["risk 1", "risk 2"],
- "video_topic": "brief description of main topic"
-}`;
- formData.append('question_format', questionFormat);
-
- // Define answer format
- const answerFormat = `{
- "stocks_mentioned": ["", ""],
- "investment_tips": ["", "", ""],
- "risks_mentioned": ["", ""],
- "video_topic": ""
-}`;
- formData.append('answer_format', answerFormat);
-
- const res2 = await fetch('https://studio.premai.io/api/v1/public/datasets/create-synthetic', {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${API_KEY}` },
- body: formData,
- });
- if (!res2.ok) throw new Error(`${res2.status}: ${await res2.text()}`);
- const { dataset_id } = await res2.json();
- console.log(` ā Dataset: ${dataset_id}`);
-
- // Wait for dataset (can take several minutes)
- console.log(' Waiting for generation (may take 5-10 minutes)...');
- let dataset;
- let checks = 0;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/datasets/${dataset_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- dataset = await res.json();
- if (checks++ % 6 === 0) {
- console.log(` Status: ${dataset.status}, ${dataset.datapoints_count} datapoints`);
- }
- } while (dataset.status === 'processing');
- console.log(` ā Ready: ${dataset.datapoints_count} datapoints\n`);
-
- // 3. Create snapshot
- console.log('3. Creating snapshot...');
- const res3 = await fetch('https://studio.premai.io/api/v1/public/snapshots/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dataset_id, split_percentage: 80 }),
- });
- if (!res3.ok) throw new Error(`${res3.status}: ${await res3.text()}`);
- const { snapshot_id } = await res3.json();
- console.log(` ā Snapshot: ${snapshot_id}\n`);
-
- // 4. Generate recommendations
- console.log('4. Generating recommendations...');
- const res4 = await fetch('https://studio.premai.io/api/v1/public/recommendations/generate', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id }),
- });
- if (!res4.ok) throw new Error(`${res4.status}: ${await res4.text()}`);
-
- let recs;
- do {
- await sleep(5000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/recommendations/${snapshot_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- recs = await res.json();
- } while (recs.status === 'processing');
-
- console.log(` ā Recommended experiments:`);
- const recommendedCount = recs.recommended_experiments.filter((e: any) => e.recommended).length;
- console.log(` Total experiments: ${recs.recommended_experiments.length}, Recommended: ${recommendedCount}`);
- recs.recommended_experiments.forEach((e: any) => {
- if (e.recommended) console.log(` - ${e.base_model_id} (LoRA: ${e.lora})`);
- });
- console.log();
-
- // 5. Create finetuning job
- console.log('5. Creating finetuning job...');
- const experiments = recs.recommended_experiments
- .filter((e: any) => e.recommended)
- .map(({ recommended, reason_for_recommendation, ...experiment }: any) => experiment);
-
- if (experiments.length === 0) {
- console.error('\nā Error: No recommended experiments found. Cannot create finetuning job.');
- process.exit(1);
- }
-
- const res5 = await fetch('https://studio.premai.io/api/v1/public/finetuning/create', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ snapshot_id, name: 'YouTube Model', experiments }),
- });
- if (!res5.ok) throw new Error(`${res5.status}: ${await res5.text()}`);
- const { job_id } = await res5.json();
- console.log(` ā Job: ${job_id}\n`);
-
- // 6. Monitor (5 minutes max)
- console.log('6. Monitoring job...');
- for (let i = 0; i < 30; i++) {
- await sleep(10000);
- const res = await fetch(`https://studio.premai.io/api/v1/public/finetuning/${job_id}`, {
- headers: { 'Authorization': `Bearer ${API_KEY}` }
- });
- if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
- const job = await res.json();
- console.log(` Status: ${job.status}`);
- job.experiments.forEach((e: any) => {
- console.log(` - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
- });
- if (job.status !== 'processing') break;
- }
-
- console.log('\nā Done!\n');
-}
-
-main().catch((err) => {
- console.error('\nā Error:', err.message);
- process.exit(1);
-});
-```
-```python Python
-#!/usr/bin/env python3
-
-"""
-Example 2: YouTube synthetic dataset workflow
-1. Create project ā 2. Generate synthetic data from YouTube ā 3. Create snapshot ā 4. Get recommendations ā 5. Run finetuning
-"""
-
-import os
-import time
-import requests
-
-API_KEY = os.getenv("API_KEY")
-YOUTUBE_URLS = [
- "https://www.youtube.com/watch?v=JH-k5f4Yclc",
- "https://www.youtube.com/watch?v=YEWhxcpMS1c",
- "https://www.youtube.com/watch?v=cb8up3HVXis",
- "https://www.youtube.com/watch?v=26xatIiMv88",
- "https://www.youtube.com/watch?v=-Da3gUdzCvs"
-]
-
-if not API_KEY:
- print("Error: API_KEY environment variable is required")
- exit(1)
-
-
-def api(endpoint: str, method: str = "GET", **kwargs):
- response = requests.request(
- method=method,
- url=f"https://studio.premai.io{endpoint}",
- headers={"Authorization": f"Bearer {API_KEY}", **kwargs.pop("headers", {})},
- **kwargs
- )
- if not response.ok:
- err = response.json() if response.content else {}
- error_msg = err.get("error", str(err)) if isinstance(err, dict) else str(err)
- raise Exception(f"{response.status_code}: {error_msg}")
- return response.json()
-
-
-def main():
- print("\n=== YouTube Synthetic Workflow ===\n")
-
- # Create project
- print("1. Creating project...")
- result = api("/api/v1/public/projects/create", method="POST", headers={"Content-Type": "application/json"}, json={"name": "Stock Analysis Project", "goal": "Extract investment insights from financial videos"})
- project_id = result["project_id"]
- print(f" ā Project: {project_id}\n")
-
- # Generate synthetic dataset
- print("2. Generating synthetic dataset from YouTube...")
- print(f" URLs: {len(YOUTUBE_URLS)} financial videos")
-
- # Define question format
- question_format = """Extract investment information from the following video transcript:
-
-{VIDEO_TRANSCRIPT}
-
-Provide the output in this JSON format:
-{
- "stocks_mentioned": ["TICKER1", "TICKER2"],
- "investment_tips": ["tip 1", "tip 2", "tip 3"],
- "risks_mentioned": ["risk 1", "risk 2"],
- "video_topic": "brief description of main topic"
-}"""
-
- # Define answer format
- answer_format = """{
- "stocks_mentioned": ["", ""],
- "investment_tips": ["", "", ""],
- "risks_mentioned": ["", ""],
- "video_topic": ""
-}"""
-
- form_data = {
- "project_id": project_id,
- "name": "Financial YouTube Dataset",
- "pairs_to_generate": "50",
- "pair_type": "qa",
- "temperature": "0.3",
- "rules[]": [
- "stocks_mentioned: List all stock ticker symbols mentioned (e.g., AAPL, TSLA, NVDA)",
- "investment_tips: Extract 3-5 specific, actionable pieces of advice from the video",
- "risks_mentioned: List any warnings, risks, or cautionary statements discussed",
- "video_topic: Write a short phrase describing the main topic of the video",
- "Only output valid JSON with no additional text before or after",
- "If a field has no relevant information, use an empty array [] or empty string \"\"",
- "Use exact quotes or close paraphrases from the video content",
- "Do not invent or infer information not explicitly stated"
- ],
- "question_format": question_format,
- "answer_format": answer_format
- }
-
- # Add YouTube URLs
- for i, url in enumerate(YOUTUBE_URLS):
- form_data[f"youtube_urls[{i}]"] = url
-
- result = api("/api/v1/public/datasets/create-synthetic", method="POST", data=form_data)
- dataset_id = result["dataset_id"]
- print(f" ā Dataset: {dataset_id}")
-
- # Wait for dataset (can take several minutes)
- print(" Waiting for generation (may take 5-10 minutes)...")
- checks = 0
- while True:
- time.sleep(5)
- dataset = api(f"/api/v1/public/datasets/{dataset_id}")
- if checks % 6 == 0:
- print(f" Status: {dataset['status']}, {dataset['datapoints_count']} datapoints")
- checks += 1
- if dataset["status"] != "processing":
- break
- print(f" ā Ready: {dataset['datapoints_count']} datapoints\n")
-
- # Create snapshot
- print("3. Creating snapshot...")
- result = api("/api/v1/public/snapshots/create", method="POST", headers={"Content-Type": "application/json"}, json={"dataset_id": dataset_id, "split_percentage": 80})
- snapshot_id = result["snapshot_id"]
- print(f" ā Snapshot: {snapshot_id}\n")
-
- # Generate recommendations
- print("4. Generating recommendations...")
- api("/api/v1/public/recommendations/generate", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id})
-
- while True:
- time.sleep(5)
- recs = api(f"/api/v1/public/recommendations/{snapshot_id}")
- if recs["status"] != "processing":
- break
-
- print(" ā Recommended experiments:")
- recommended_count = sum(1 for e in recs["recommended_experiments"] if e["recommended"])
- print(f" Total experiments: {len(recs['recommended_experiments'])}, Recommended: {recommended_count}")
- for e in recs["recommended_experiments"]:
- if e["recommended"]:
- print(f" - {e['base_model_id']} (LoRA: {e['lora']})")
- print()
-
- # Create finetuning job
- print("5. Creating finetuning job...")
- experiments = [
- {k: v for k, v in exp.items() if k not in ["recommended", "reason_for_recommendation"]}
- for exp in recs["recommended_experiments"] if exp["recommended"]
- ]
-
- if not experiments:
- print("\nā Error: No recommended experiments found. Cannot create finetuning job.")
- exit(1)
-
- result = api("/api/v1/public/finetuning/create", method="POST", headers={"Content-Type": "application/json"}, json={"snapshot_id": snapshot_id, "name": "YouTube Model", "experiments": experiments})
- job_id = result["job_id"]
- print(f" ā Job: {job_id}\n")
-
- # Monitor (5 minutes max)
- print("6. Monitoring job...")
- for i in range(30):
- time.sleep(10)
- job = api(f"/api/v1/public/finetuning/{job_id}")
- print(f" Status: {job['status']}")
- for exp in job["experiments"]:
- print(f" - Exp #{exp['experiment_number']}: {exp['status']} {exp.get('model_id', '')}")
- if job["status"] != "processing":
- break
-
- print("\nā Done!\n")
-
-
-if __name__ == "__main__":
- try:
- main()
- except Exception as err:
- print(f"\nā Error: {err}")
- exit(1)
-```
-
diff --git a/docs.json b/docs.json
index 327946a72c..202fe4c2f0 100644
--- a/docs.json
+++ b/docs.json
@@ -126,16 +126,8 @@
"group": "API Get Started",
"pages": [
"api-reference/introduction",
- "api-reference/api-key"
- ]
- },
- {
- "group": "Walkthroughs šÆ",
- "pages": [
- "api-reference/walkthroughs/jsonl-workflow",
- "api-reference/walkthroughs/youtube-synthetic-workflow",
- "api-reference/walkthroughs/pdf-synthetic-workflow",
- "api-reference/walkthroughs/continuous-finetuning-workflow"
+ "api-reference/api-key",
+ "api-reference/github-tutorials"
]
},
{