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" ] }, {