Exemple de SDK AI pe bază de șablon
Fiecare șablon de proiect DojoCode poate folosi SDK-ul DojoCode pentru a apela modele AI reale. Această pagină conține un exemplu complet, gata de copiat și lipit, pentru fiecare șablon: ce fișier să creezi sau să înlocuiești și codul pe care să îl adaugi. Exemplele folosesc modalități diferite, astfel încât, împreună, le prezintă pe toate cele nouă.
Fiecare exemplu respectă aceleași reguli:
- Citește conexiunea cu numele exacte pentru șablonul său — nimic de configurat și nicio cheie API de lipit.
- Pornește o generare doar când dai clic (previzualizarea se reîncarcă la fiecare salvare, așa că o generare la încărcarea paginii te-ar taxa la nesfârșit).
- Gestionează ambele răspunsuri —
200(finalizat) și202(încă rulează, interogat la fiecare 2 secunde) — și afișează mesajul de eroare atunci când ceva eșuează.
Sau întreabă direct asistentul
Deschide panoul AI Chat din proiectul tău și descrie funcționalitatea — asistentul va scrie acest cod pentru șablonul tău. Exemplele de mai jos sunt exact ceea ce produce acesta, gata de citit, copiat și adaptat.
| Șablon | Unde se pune codul | Exemplu | Modalitate |
|---|---|---|---|
| React | /App.jsx | Generator de imagini | text-to-image |
| React TS | /App.tsx | Evaluator de fotografii | image-to-text |
| Vue | /App.vue | Compozitor de muzică | text-to-music |
| Vue TS | /App.vue | Restilizator de fotografii | image-to-image |
| Svelte | /App.svelte | Întreabă orice | text-to-text |
| SvelteKit | /src/routes/+page.svelte | Creator de stickere | text-to-image |
| SvelteKit TS | /src/routes/+page.svelte | Generator de clipuri video | text-to-video |
| SolidJS | /App.jsx | Generator de două imagini | text-to-image |
| SolidJS TS | /App.tsx | Generator de chestionare (JSON) | text-to-text |
| Vanilla JS | /index.js | Notițe vocale în text | speech-to-text |
| Vanilla TS | /index.ts | Narator cu voce clonată | text-to-speech |
| Remix | /app/routes/_index.jsx | Generator de postere | text-to-image |
| Remix TS | /app/routes/_index.tsx | Animator de fotografii | image-to-video |
| React Native | /App.jsx | Generator de imagini de fundal | text-to-image |
| React Native TS | /App.tsx | Tutor de programare | text-to-text |
| Next.js | /src/app/page.jsx | Generator de clipuri video | text-to-video |
| Next.js TS | /src/app/page.tsx | Compozitor de muzică | text-to-music |
| Astro | /src/pages/index.astro | Scriitor de povești | text-to-text |
| Astro TS | /src/pages/index.astro | Generator de imagini | text-to-image |
| Angular | /app.component.ts | Generator de imagini | text-to-image |
| Fastify | /src/app.ts | Ruta GET /fact | text-to-text |
| Hono | /src/app.ts | Ruta GET /poster | text-to-image |
| NestJS | /src/app.service.ts | Ruta POST /summarize | text-to-text |
| Node.js | /main.js | Curiozitate | text-to-text |
| Node.js TS | /main.ts | Curiozitate | text-to-text |
| Python — browser preview | /main.py + /index.html | Generator de cărți poștale | text-to-image |
| Python — Run | /main.py | Imagine cu interogare (polling) | text-to-image |
| Java | /Main.java | Curiozitate | text-to-text |
| Go | /main.go | Curiozitate | text-to-text |
| C# | /Main.cs | Curiozitate | text-to-text |
| PHP | /main.php | Curiozitate | text-to-text |
| Ruby | /main.rb | Curiozitate | text-to-text |
| Rust | /main.rs | Curiozitate | text-to-text |
C și C++
Proiectele în C și C++ nu pot apela încă SDK-ul: mediul lor de rulare (Run) nu are un client HTTPS încorporat. Folosește unul dintre șabloanele de mai sus pentru funcționalitățile AI.
Șabloane pentru browser (Vite)
React, Vue, Svelte, SvelteKit, SolidJS, Vanilla, Remix și React Native rulează toate pe Vite și citesc conexiunea din import.meta.env.VITE_DOJOCODE_API_URL și import.meta.env.VITE_DOJOCODE_AI_TOKEN.
Helper-ul pentru șabloanele Vite
Salvează helper-ul o singură dată lângă componentele tale — /dojocode-ai.js în șabloanele JavaScript, /dojocode-ai.ts în cele TypeScript (în SvelteKit pune-l în /src/lib/, iar în Remix în /app/). Exemplele de mai jos îl importă.
const API_URL = import.meta.env.VITE_DOJOCODE_API_URL;
const API_TOKEN = import.meta.env.VITE_DOJOCODE_AI_TOKEN;
const FINISHED = ['succeeded', 'failed', 'cancelled'];
async function request(path, options = {}) {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data;
}
/** Submit a generation and resolve with its output (handles 200 and 202). */
export async function generate(modality, input) {
let record = await request('/ai-generation/generations', {
method: 'POST',
body: JSON.stringify({ modality, input })
});
while (!FINISHED.includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output;
}
/** Downscale a picked image to at most 1024 px and return a JPEG data URI. */
export function imageToDataUri(file, maxSide = 1024) {
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.onload = () => {
const scale = Math.min(1, maxSide / Math.max(img.width, img.height));
const canvas = document.createElement('canvas');
canvas.width = Math.round(img.width * scale);
canvas.height = Math.round(img.height * scale);
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
URL.revokeObjectURL(img.src);
resolve(canvas.toDataURL('image/jpeg', 0.8));
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
}
/** Turn a Blob or File (audio, image) into a data URI. */
export function blobToDataUri(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}const API_URL = import.meta.env.VITE_DOJOCODE_API_URL as string;
const API_TOKEN = import.meta.env.VITE_DOJOCODE_AI_TOKEN as string;
export type Modality =
| 'text-to-text'
| 'image-to-text'
| 'text-to-image'
| 'image-to-image'
| 'text-to-video'
| 'image-to-video'
| 'text-to-music'
| 'text-to-speech'
| 'speech-to-text';
interface GenerationRecord {
id: string;
status: 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
output: unknown;
error: { message: string } | null;
}
async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data as GenerationRecord;
}
/** Submit a generation and resolve with its output (handles 200 and 202). */
export async function generate<T = unknown>(modality: Modality, input: Record<string, unknown>): Promise<T> {
let record = await request('/ai-generation/generations', {
method: 'POST',
body: JSON.stringify({ modality, input })
});
while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output as T;
}
/** Downscale a picked image to at most 1024 px and return a JPEG data URI. */
export function imageToDataUri(file: File, maxSide = 1024): Promise<string> {
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.onload = () => {
const scale = Math.min(1, maxSide / Math.max(img.width, img.height));
const canvas = document.createElement('canvas');
canvas.width = Math.round(img.width * scale);
canvas.height = Math.round(img.height * scale);
canvas.getContext('2d')?.drawImage(img, 0, 0, canvas.width, canvas.height);
URL.revokeObjectURL(img.src);
resolve(canvas.toDataURL('image/jpeg', 0.8));
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
}
/** Turn a Blob or File (audio, image) into a data URI. */
export function blobToDataUri(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}React
Înlocuiește /App.jsx — un generator de imagini (text-to-image, 3 tokeni AI per imagine):
import { useState } from 'react';
import { generate } from './dojocode-ai';
export default function App() {
const [prompt, setPrompt] = useState('');
const [images, setImages] = useState([]);
const [status, setStatus] = useState('');
const [busy, setBusy] = useState(false);
async function handleGenerate() {
setBusy(true);
setStatus('Generating…');
try {
setImages(await generate('text-to-image', { prompt, aspect_ratio: '16:9' }));
setStatus('');
} catch (error) {
setStatus(error.message);
} finally {
setBusy(false);
}
}
return (
<main style={{ maxWidth: 640, margin: '2rem auto', fontFamily: 'sans-serif' }}>
<h1>Image generator</h1>
<input value={prompt} onChange={(event) => setPrompt(event.target.value)} placeholder="A lighthouse in a storm, oil painting" style={{ width: '100%' }} />
<button onClick={handleGenerate} disabled={busy || !prompt.trim()}>
Generate (3 AI tokens)
</button>
<p>{status}</p>
{images.map((url) => (
<img key={url} src={url} alt={prompt} style={{ width: '100%', borderRadius: 8 }} />
))}
</main>
);
}React TS
Înlocuiește /App.tsx — un evaluator de fotografii (image-to-text, 2 tokeni AI):
import { useState, type ChangeEvent } from 'react';
import { generate, imageToDataUri } from './dojocode-ai';
export default function App() {
const [image, setImage] = useState<string | null>(null);
const [answer, setAnswer] = useState('');
const [busy, setBusy] = useState(false);
async function handleFile(event: ChangeEvent<HTMLInputElement>) {
const file = event.target.files?.[0];
if (!file) return;
setImage(await imageToDataUri(file));
setAnswer('');
}
async function handleGrade() {
if (!image) return;
setBusy(true);
setAnswer('Looking at your photo…');
try {
setAnswer(
await generate<string>('image-to-text', {
prompt: 'Grade this photo from 1 to 10, then give three short tips to improve it.',
image
})
);
} catch (error) {
setAnswer((error as Error).message);
} finally {
setBusy(false);
}
}
return (
<main style={{ maxWidth: 640, margin: '2rem auto', fontFamily: 'sans-serif' }}>
<h1>Photo grader</h1>
<input type="file" accept="image/*" onChange={handleFile} />
{image && <img src={image} alt="Your photo" style={{ width: '100%', marginTop: 12 }} />}
<button onClick={handleGrade} disabled={!image || busy}>
Grade my photo (2 AI tokens)
</button>
<p style={{ whiteSpace: 'pre-wrap' }}>{answer}</p>
</main>
);
}Vue
Înlocuiește /App.vue — un compozitor de muzică (text-to-music, 4 tokeni AI per 10 secunde):
<script setup>
import { ref } from 'vue';
import { generate } from './dojocode-ai';
const prompt = ref('Upbeat lo-fi hip hop with warm piano chords and a relaxed drum groove');
const duration = ref(8);
const audioUrl = ref('');
const status = ref('');
const busy = ref(false);
async function compose() {
busy.value = true;
status.value = 'Composing — this takes about a minute…';
try {
audioUrl.value = await generate('text-to-music', { prompt: prompt.value, duration: duration.value, output_format: 'mp3' });
status.value = '';
} catch (error) {
status.value = error.message;
} finally {
busy.value = false;
}
}
</script>
<template>
<main>
<h1>Music composer</h1>
<textarea v-model="prompt" rows="3"></textarea>
<select v-model.number="duration">
<option :value="8">8 seconds (4 AI tokens)</option>
<option :value="15">15 seconds (8 AI tokens)</option>
<option :value="30">30 seconds (12 AI tokens)</option>
</select>
<button :disabled="busy || !prompt.trim()" @click="compose">Compose</button>
<p>{{ status }}</p>
<audio v-if="audioUrl" :src="audioUrl" controls></audio>
</main>
</template>Vue TS
Înlocuiește /App.vue — un stilizator de fotografii (image-to-image, 5 tokeni AI per imagine):
<script setup lang="ts">
import { ref } from 'vue';
import { generate, imageToDataUri } from './dojocode-ai';
const source = ref<string | null>(null);
const prompt = ref('Turn this photo into a watercolor painting');
const results = ref<string[]>([]);
const status = ref('');
async function pick(event: Event) {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) source.value = await imageToDataUri(file);
}
async function restyle() {
if (!source.value) return;
status.value = 'Painting…';
try {
results.value = await generate<string[]>('image-to-image', {
prompt: prompt.value,
image: source.value,
prompt_strength: 0.6
});
status.value = '';
} catch (error) {
status.value = (error as Error).message;
}
}
</script>
<template>
<main>
<h1>Photo restyler</h1>
<input type="file" accept="image/*" @change="pick" />
<input v-model="prompt" />
<button :disabled="!source" @click="restyle">Restyle (5 AI tokens)</button>
<p>{{ status }}</p>
<img v-for="url in results" :key="url" :src="url" width="480" />
</main>
</template>Svelte
Înlocuiește /App.svelte — întreabă orice (text-to-text, 1 token AI):
<script>
import { generate } from './dojocode-ai';
let prompt = '';
let answer = '';
let busy = false;
async function ask() {
busy = true;
answer = 'Thinking…';
try {
answer = await generate('text-to-text', { prompt, max_tokens: 400 });
} catch (error) {
answer = error.message;
} finally {
busy = false;
}
}
</script>
<main>
<h1>Ask anything</h1>
<textarea bind:value={prompt} rows="4" placeholder="Explain recursion like I'm ten"></textarea>
<button on:click={ask} disabled={busy || !prompt.trim()}>Ask (1 AI token)</button>
<p style="white-space: pre-wrap">{answer}</p>
</main>SvelteKit
Salvează fișierul ajutător ca /src/lib/dojocode-ai.js, apoi înlocuiește /src/routes/+page.svelte — un generator de stickere (text-to-image, 3 tokeni AI per imagine):
<script>
import { generate } from '$lib/dojocode-ai';
let subject = '';
let stickers = [];
let status = '';
async function makeStickers() {
status = 'Drawing 2 stickers…';
try {
stickers = await generate('text-to-image', {
prompt: `A cute die-cut sticker of ${subject}, thick white border, flat colors`,
num_outputs: 2,
aspect_ratio: '1:1'
});
status = '';
} catch (error) {
status = error.message;
}
}
</script>
<h1>Sticker maker</h1>
<input bind:value={subject} placeholder="a sleepy cat astronaut" />
<button on:click={makeStickers} disabled={!subject.trim()}>Make 2 stickers (6 AI tokens)</button>
<p>{status}</p>
{#each stickers as url}
<img src={url} alt={subject} width="240" />
{/each}SvelteKit TS
Salvează fișierul ajutător ca /src/lib/dojocode-ai.ts, apoi înlocuiește /src/routes/+page.svelte — un generator video (text-to-video, 12 tokeni AI pentru ~4 secunde):
<script lang="ts">
import { generate } from '$lib/dojocode-ai';
let prompt = '';
let videoUrl = '';
let status = '';
async function render() {
status = 'Rendering — this takes a minute or two…';
try {
const [url] = await generate<string[]>('text-to-video', { prompt, aspect_ratio: '16:9' });
videoUrl = url;
status = '';
} catch (error) {
status = (error as Error).message;
}
}
</script>
<h1>Video generator</h1>
<textarea bind:value={prompt} rows="4" placeholder="Describe the scene, the motion, the camera and the light"></textarea>
<button on:click={render} disabled={!prompt.trim()}>Generate video (12 AI tokens)</button>
<p>{status}</p>
{#if videoUrl}
<video src={videoUrl} controls width="640"></video>
{/if}SolidJS
Înlocuiește /App.jsx — două imagini simultan (text-to-image, 3 tokeni AI per imagine):
import { createSignal, For } from 'solid-js';
import { generate } from './dojocode-ai';
export default function App() {
const [prompt, setPrompt] = createSignal('');
const [images, setImages] = createSignal([]);
const [status, setStatus] = createSignal('');
async function handleGenerate() {
setStatus('Generating 2 images…');
try {
setImages(await generate('text-to-image', { prompt: prompt(), num_outputs: 2 }));
setStatus('');
} catch (error) {
setStatus(error.message);
}
}
return (
<main>
<h1>Two-image generator</h1>
<input value={prompt()} onInput={(event) => setPrompt(event.currentTarget.value)} placeholder="A cozy cabin in the snow" />
<button onClick={handleGenerate} disabled={!prompt().trim()}>
Generate 2 images (6 AI tokens)
</button>
<p>{status()}</p>
<For each={images()}>{(url) => <img src={url} width={320} />}</For>
</main>
);
}SolidJS TS
Înlocuiește /App.tsx — un generator de quiz-uri care solicită JSON (text-to-text, 1 token AI):
import { createSignal, For, Show } from 'solid-js';
import { generate } from './dojocode-ai';
interface Question {
question: string;
options: string[];
answer: number;
}
/** The model may wrap JSON in a code fence or add text around it. */
function parseQuiz(text: string): Question[] {
const start = text.indexOf('[');
const end = text.lastIndexOf(']');
try {
const data = JSON.parse(text.slice(start, end + 1));
return Array.isArray(data) ? data : [];
} catch {
return [];
}
}
export default function App() {
const [topic, setTopic] = createSignal('');
const [quiz, setQuiz] = createSignal<Question[]>([]);
const [status, setStatus] = createSignal('');
async function makeQuiz() {
setStatus('Writing the quiz…');
try {
const text = await generate<string>('text-to-text', {
prompt: `Write 3 multiple-choice questions about ${topic()}. Reply ONLY with a JSON array of objects with "question", "options" (4 strings) and "answer" (the index of the correct option).`,
max_tokens: 800,
temperature: 0.4
});
const questions = parseQuiz(text);
setQuiz(questions);
setStatus(questions.length ? '' : 'The answer was not valid JSON — try again.');
} catch (error) {
setStatus((error as Error).message);
}
}
return (
<main>
<h1>Quiz generator</h1>
<input value={topic()} onInput={(event) => setTopic(event.currentTarget.value)} placeholder="the solar system" />
<button onClick={makeQuiz} disabled={!topic().trim()}>Make a quiz (1 AI token)</button>
<p>{status()}</p>
<For each={quiz()}>
{(item) => (
<section>
<h3>{item.question}</h3>
<ol type="A">
<For each={item.options}>{(option, index) => <li>{option}{index() === item.answer ? ' ✓' : ''}</li>}</For>
</ol>
</section>
)}
</For>
<Show when={!quiz().length && !status()}>
<p>Pick a topic to start.</p>
</Show>
</main>
);
}Vanilla JS
Înlocuiește /index.js — note vocale în text (speech-to-text, 2 tokeni AI per înregistrare). Pagina este construită din JavaScript, deci nu este necesar un fișier HTML:
import { generate, blobToDataUri } from './dojocode-ai.js';
document.body.innerHTML = `
<main style="max-width: 640px; margin: 2rem auto; font-family: sans-serif">
<h1>Voice notes to text</h1>
<input type="file" id="recording" accept="audio/*">
<button id="transcribe">Transcribe (2 AI tokens)</button>
<pre id="result" style="white-space: pre-wrap"></pre>
</main>
`;
const button = document.querySelector('#transcribe');
const result = document.querySelector('#result');
button.addEventListener('click', async () => {
const file = document.querySelector('#recording').files[0];
if (!file) {
result.textContent = 'Pick an audio file first.';
return;
}
button.disabled = true;
result.textContent = 'Transcribing…';
try {
const { transcription, detected_language } = await generate('speech-to-text', {
audio: await blobToDataUri(file),
language: 'auto'
});
result.textContent = `[${detected_language}] ${transcription}`;
} catch (error) {
result.textContent = error.message;
} finally {
button.disabled = false;
}
});Vanilla TS
Înlocuiește /index.ts — un narator care vorbește cu o voce clonată (text-to-speech, 3 tokenuri AI per 1.000 de caractere):
import { generate, blobToDataUri } from './dojocode-ai';
document.body.innerHTML = `
<main style="max-width: 640px; margin: 2rem auto; font-family: sans-serif">
<h1>Narrator</h1>
<p><label>Voice sample (10–30 s): <input type="file" id="sample" accept="audio/*"></label></p>
<p><label>What the sample says: <input id="sampleText" style="width: 100%"></label></p>
<textarea id="text" rows="4" style="width: 100%" placeholder="Text to read aloud"></textarea>
<button id="speak">Speak (3 AI tokens per 1,000 characters)</button>
<p id="status"></p>
<audio id="player" controls hidden></audio>
</main>
`;
const status = document.querySelector<HTMLParagraphElement>('#status')!;
const player = document.querySelector<HTMLAudioElement>('#player')!;
document.querySelector('#speak')!.addEventListener('click', async () => {
const sample = document.querySelector<HTMLInputElement>('#sample')!.files?.[0];
const sampleText = document.querySelector<HTMLInputElement>('#sampleText')!.value;
const text = document.querySelector<HTMLTextAreaElement>('#text')!.value;
if (!sample || !sampleText.trim() || !text.trim()) {
status.textContent = 'Add a voice sample, its exact words and the text to read.';
return;
}
status.textContent = 'Generating the voice — this can take a minute…';
try {
player.src = await generate<string>('text-to-speech', {
text,
speaker_reference: await blobToDataUri(sample),
text_reference: sampleText
});
player.hidden = false;
status.textContent = '';
} catch (error) {
status.textContent = (error as Error).message;
}
});Remix
Salvează helper-ul ca /app/dojocode-ai.js, apoi înlocuiește /app/routes/_index.jsx — un generator de postere (text-to-image, 3 tokenuri AI per imagine):
import { useState } from 'react';
import { generate } from '../dojocode-ai';
export default function Index() {
const [title, setTitle] = useState('');
const [poster, setPoster] = useState('');
const [status, setStatus] = useState('');
async function handleGenerate() {
setStatus('Designing your poster…');
try {
const [url] = await generate('text-to-image', {
prompt: `A bold retro travel poster for "${title}", screen-print style, strong typography`,
aspect_ratio: '2:3'
});
setPoster(url);
setStatus('');
} catch (error) {
setStatus(error.message);
}
}
return (
<main>
<h1>Poster generator</h1>
<input value={title} onChange={(event) => setTitle(event.target.value)} placeholder="Visit Mars" />
<button onClick={handleGenerate} disabled={!title.trim()}>
Generate (3 AI tokens)
</button>
<p>{status}</p>
{poster && <img src={poster} alt={title} width={360} />}
</main>
);
}Remix TS
Salvează helper-ul ca /app/dojocode-ai.ts, apoi înlocuiește /app/routes/_index.tsx — un animator de fotografii (image-to-video, 12 tokenuri AI per ~4 secunde):
import { useState, type ChangeEvent } from 'react';
import { generate, imageToDataUri } from '../dojocode-ai';
export default function Index() {
const [image, setImage] = useState<string | null>(null);
const [prompt, setPrompt] = useState('The scene slowly comes alive, gentle camera push-in, soft wind');
const [videoUrl, setVideoUrl] = useState('');
const [status, setStatus] = useState('');
async function handleFile(event: ChangeEvent<HTMLInputElement>) {
const file = event.target.files?.[0];
if (file) setImage(await imageToDataUri(file));
}
async function animate() {
if (!image) return;
setStatus('Animating — this takes a minute or two…');
try {
const [url] = await generate<string[]>('image-to-video', { prompt, image });
setVideoUrl(url);
setStatus('');
} catch (error) {
setStatus((error as Error).message);
}
}
return (
<main>
<h1>Photo animator</h1>
<input type="file" accept="image/*" onChange={handleFile} />
<textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} rows={3} />
<button onClick={animate} disabled={!image}>
Animate (12 AI tokens)
</button>
<p>{status}</p>
{videoUrl && <video src={videoUrl} controls width={480} />}
</main>
);
}React Native
Înlocuiește /App.jsx — un generator de imagini de fundal pentru telefon (text-to-image, 3 tokenuri AI):
import { useState } from 'react';
import { View, Text, TextInput, Pressable, Image, StyleSheet } from 'react-native';
import { generate } from './dojocode-ai';
export default function App() {
const [prompt, setPrompt] = useState('');
const [imageUrl, setImageUrl] = useState(null);
const [status, setStatus] = useState('');
async function handleGenerate() {
setStatus('Generating…');
try {
const [url] = await generate('text-to-image', { prompt, aspect_ratio: '9:16' });
setImageUrl(url);
setStatus('');
} catch (error) {
setStatus(error.message);
}
}
return (
<View style={styles.container}>
<Text style={styles.title}>Wallpaper generator</Text>
<TextInput value={prompt} onChangeText={setPrompt} placeholder="Neon city at night" style={styles.input} />
<Pressable onPress={handleGenerate} style={styles.button}>
<Text style={styles.buttonText}>Generate (3 AI tokens)</Text>
</Pressable>
<Text>{status}</Text>
{imageUrl && <Image source={{ uri: imageUrl }} style={styles.wallpaper} />}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', padding: 24, gap: 12 },
title: { fontSize: 24, fontWeight: '600' },
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 10, width: 280 },
button: { backgroundColor: '#6c5ce7', borderRadius: 8, paddingVertical: 12, paddingHorizontal: 20 },
buttonText: { color: 'white', fontWeight: '600' },
wallpaper: { width: 216, height: 384, borderRadius: 12 }
});React Native și imaginile
Image din 'react-native' eclipsează elementul Image al browserului, așa că helper-ul creează elemente de tip imagine folosind document.createElement('img'). Nu folosi niciodată new Image() în codul React Native.
React Native TS
Înlocuiește /App.tsx — un tutor de programare (text-to-text, 1 token AI):
import { useState } from 'react';
import { ScrollView, Text, TextInput, Pressable, StyleSheet } from 'react-native';
import { generate } from './dojocode-ai';
export default function App() {
const [question, setQuestion] = useState('');
const [answer, setAnswer] = useState('');
const [busy, setBusy] = useState(false);
async function ask() {
setBusy(true);
setAnswer('Thinking…');
try {
setAnswer(
await generate<string>('text-to-text', {
prompt: question,
system_prompt: 'You are a patient coding tutor. Answer in at most five sentences with one small example.',
max_tokens: 400
})
);
} catch (error) {
setAnswer((error as Error).message);
} finally {
setBusy(false);
}
}
return (
<ScrollView contentContainerStyle={styles.container}>
<Text style={styles.title}>Coding tutor</Text>
<TextInput value={question} onChangeText={setQuestion} placeholder="What is a closure?" multiline style={styles.input} />
<Pressable onPress={ask} disabled={busy || !question.trim()} style={styles.button}>
<Text style={styles.buttonText}>Ask (1 AI token)</Text>
</Pressable>
<Text style={styles.answer}>{answer}</Text>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { padding: 24, gap: 12 },
title: { fontSize: 24, fontWeight: '600' },
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 10, minHeight: 80 },
button: { backgroundColor: '#6c5ce7', borderRadius: 8, padding: 12, alignItems: 'center' },
buttonText: { color: 'white', fontWeight: '600' },
answer: { fontSize: 16, lineHeight: 22 }
});Șabloane Next.js
Doar variabilele NEXT_PUBLIC_ ajung în codul browserului, iar import.meta.env nu există în Next.js. Apelează SDK-ul dintr-un Client Component ('use client').
Next.js
Creează /src/lib/dojocode-ai.js și înlocuiește /src/app/page.jsx — un generator video (text-to-video, 12 token-uri AI per ~4 secunde):
const API_URL = process.env.NEXT_PUBLIC_DOJOCODE_API_URL;
const API_TOKEN = process.env.NEXT_PUBLIC_DOJOCODE_AI_TOKEN;
async function request(path, options = {}) {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data;
}
export async function generate(modality, input) {
let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output;
}'use client';
import { useState } from 'react';
import { generate } from '../lib/dojocode-ai';
export default function Page() {
const [prompt, setPrompt] = useState('');
const [videoUrl, setVideoUrl] = useState('');
const [status, setStatus] = useState('');
async function handleGenerate() {
setStatus('Rendering the video — this takes a minute or two…');
try {
const [url] = await generate('text-to-video', { prompt, aspect_ratio: '16:9', length: 97 });
setVideoUrl(url);
setStatus('');
} catch (error) {
setStatus(error.message);
}
}
return (
<main>
<h1>Video generator</h1>
<textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} rows={4} placeholder="A slow drone shot over a misty forest at sunrise" />
<button onClick={handleGenerate} disabled={!prompt.trim()}>
Generate video (12 AI tokens)
</button>
<p>{status}</p>
{videoUrl && <video src={videoUrl} controls width={640} />}
</main>
);
}Next.js TS
Creează /src/lib/dojocode-ai.ts și înlocuiește /src/app/page.tsx — un compozitor de muzică (text-to-music, 4 token-uri AI per 10 secunde):
const API_URL = process.env.NEXT_PUBLIC_DOJOCODE_API_URL;
const API_TOKEN = process.env.NEXT_PUBLIC_DOJOCODE_AI_TOKEN;
interface GenerationRecord {
id: string;
status: string;
output: unknown;
error: { message: string } | null;
}
async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data as GenerationRecord;
}
export async function generate<T = unknown>(modality: string, input: Record<string, unknown>): Promise<T> {
let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output as T;
}'use client';
import { useState } from 'react';
import { generate } from '../lib/dojocode-ai';
export default function Page() {
const [prompt, setPrompt] = useState('Epic orchestral trailer music with big drums and strings');
const [duration, setDuration] = useState(15);
const [audioUrl, setAudioUrl] = useState('');
const [status, setStatus] = useState('');
async function compose() {
setStatus('Composing…');
try {
setAudioUrl(await generate<string>('text-to-music', { prompt, duration, output_format: 'mp3' }));
setStatus('');
} catch (error) {
setStatus((error as Error).message);
}
}
return (
<main>
<h1>Music composer</h1>
<textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} rows={3} />
<select value={duration} onChange={(event) => setDuration(Number(event.target.value))}>
<option value={8}>8 seconds (4 AI tokens)</option>
<option value={15}>15 seconds (8 AI tokens)</option>
<option value={30}>30 seconds (12 AI tokens)</option>
</select>
<button onClick={compose}>Compose</button>
<p>{status}</p>
{audioUrl && <audio src={audioUrl} controls />}
</main>
);
}Șabloane Astro
Astro expune variabilele PUBLIC_ către codul <script> din browser (numele VITE_ sunt undefined acolo). Scriptul <script> al paginii rulează în browser și poate importa funcția ajutătoare.
Astro
Creează /src/lib/dojocode-ai.js și înlocuiește /src/pages/index.astro — un scriitor de povești (text-to-text, 1 token AI):
const API_URL = import.meta.env.PUBLIC_DOJOCODE_API_URL;
const API_TOKEN = import.meta.env.PUBLIC_DOJOCODE_AI_TOKEN;
async function request(path, options = {}) {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data;
}
export async function generate(modality, input) {
let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output;
}---
const title = 'Story writer';
---
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<input id="topic" placeholder="a dragon who is afraid of the dark" />
<button id="write">Write a story (1 AI token)</button>
<p id="story" style="white-space: pre-wrap"></p>
<script>
import { generate } from '../lib/dojocode-ai.js';
const topic = document.querySelector('#topic');
const story = document.querySelector('#story');
document.querySelector('#write').addEventListener('click', async () => {
story.textContent = 'Writing…';
try {
story.textContent = await generate('text-to-text', {
prompt: `Write a short bedtime story for kids about ${topic.value}.`,
max_tokens: 600
});
} catch (error) {
story.textContent = error.message;
}
});
</script>
</body>
</html>Astro TS
Creează /src/lib/dojocode-ai.ts (helper-ul TS Next.js de mai sus, cu import.meta.env.PUBLIC_DOJOCODE_API_URL și import.meta.env.PUBLIC_DOJOCODE_AI_TOKEN în primele sale două linii) și înlocuiește /src/pages/index.astro — un generator de imagini (text-to-image, 3 tokeni AI):
const API_URL = import.meta.env.PUBLIC_DOJOCODE_API_URL as string;
const API_TOKEN = import.meta.env.PUBLIC_DOJOCODE_AI_TOKEN as string;
interface GenerationRecord {
id: string;
status: string;
output: unknown;
error: { message: string } | null;
}
async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data as GenerationRecord;
}
export async function generate<T = unknown>(modality: string, input: Record<string, unknown>): Promise<T> {
let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output as T;
}---
const title = 'Image generator';
---
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<input id="prompt" placeholder="A koi pond in autumn, ukiyo-e print" />
<button id="generate">Generate (3 AI tokens)</button>
<p id="status"></p>
<img id="result" width="480" hidden />
<script>
import { generate } from '../lib/dojocode-ai';
const prompt = document.querySelector('#prompt') as HTMLInputElement;
const status = document.querySelector('#status') as HTMLParagraphElement;
const result = document.querySelector('#result') as HTMLImageElement;
document.querySelector('#generate')?.addEventListener('click', async () => {
status.textContent = 'Generating…';
try {
const [url] = await generate<string[]>('text-to-image', { prompt: prompt.value });
result.src = url;
result.hidden = false;
status.textContent = '';
} catch (error) {
status.textContent = (error as Error).message;
}
});
</script>
</body>
</html>Șablon Angular
Platforma adaugă dojocode-env.ts lângă app.component.ts — este ascuns în arborele de fișiere, așa că doar importă-l. Variabilele globale process.env, import.meta.env și window nu sunt disponibile în Angular. FormsModule este deja importat în app.module.ts, așa că [(ngModel)] funcționează direct.
Angular
Creează /dojocode-ai.ts și înlocuiește /app.component.ts și /app.component.html — un generator de imagini (text-to-image, 3 tokeni AI):
import { DOJOCODE_ENV } from './dojocode-env';
const API_URL = DOJOCODE_ENV['DOJOCODE_API_URL'];
const API_TOKEN = DOJOCODE_ENV['DOJOCODE_AI_TOKEN'];
interface GenerationRecord {
id: string;
status: string;
output: unknown;
error: { message: string } | null;
}
async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
const response = await fetch(`${API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data as GenerationRecord;
}
export async function generate<T = unknown>(modality: string, input: Record<string, unknown>): Promise<T> {
let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output as T;
}import { Component } from '@angular/core';
import { generate } from './dojocode-ai';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: []
})
export class AppComponent {
prompt = '';
images: string[] = [];
status = '';
busy = false;
async generateImages(): Promise<void> {
this.busy = true;
this.status = 'Generating…';
try {
this.images = await generate<string[]>('text-to-image', { prompt: this.prompt });
this.status = '';
} catch (error) {
this.status = (error as Error).message;
} finally {
this.busy = false;
}
}
}<main>
<h1>Image generator</h1>
<input [(ngModel)]="prompt" placeholder="A paper boat on a blue pond" />
<button (click)="generateImages()" [disabled]="busy || !prompt.trim()">Generate (3 AI tokens)</button>
<p>{{ status }}</p>
<img *ngFor="let url of images" [src]="url" width="480" />
</main>Servere Node.js în previzualizare
NestJS, Fastify și Hono rulează în previzualizare cu conexiunea în process.env.DOJOCODE_API_URL și process.env.DOJOCODE_AI_TOKEN. Acestea apelează SDK-ul folosind funcția nativă fetch — nu este necesară nicio bibliotecă HTTP. Salvează acest helper ca /src/dojocode-ai.ts și testează rutele din previzualizare sau din Testerul API:
// src/dojocode-ai.ts
const FINISHED = ['succeeded', 'failed', 'cancelled'];
async function request(path: string, options: RequestInit = {}): Promise<any> {
const response = await fetch(`${process.env.DOJOCODE_API_URL}${path}`, {
...options,
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.DOJOCODE_AI_TOKEN}` }
});
const data = await response.json().catch(() => null);
if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
return data;
}
/** Submit a generation and resolve with its output (handles 200 and 202). */
export async function generate(modality: string, input: Record<string, unknown>): Promise<unknown> {
let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
while (!FINISHED.includes(record.status)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
record = await request(`/ai-generation/generations/${record.id}`);
}
if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
return record.output;
}Fastify
Înlocuiește /src/app.ts (main.ts continuă să apeleze buildApp()) — GET /fact?topic=octopus (text-to-text, 1 token AI per apel):
import Fastify, { type FastifyInstance } from 'fastify';
import { generate } from './dojocode-ai';
export function buildApp(): FastifyInstance {
const app = Fastify({ logger: false });
app.get('/', async () => 'Hello, World! Try GET /fact?topic=octopus');
app.get<{ Querystring: { topic?: string } }>('/fact', async (request, reply) => {
try {
const fact = await generate('text-to-text', {
prompt: `Write one surprising fact about ${request.query.topic ?? 'space'}.`,
max_tokens: 120
});
return { fact };
} catch (error) {
return reply.status(502).send({ error: (error as Error).message });
}
});
return app;
}Hono
Înlocuiește /src/app.ts (main.ts continuă să deservească app exportat) — GET /poster?prompt=retro+robot (text-to-image, 3 tokenuri AI per apel):
import { Hono } from 'hono';
import { generate } from './dojocode-ai';
export const app = new Hono();
app.get('/', (c) => c.text('Hello, World! Try GET /poster?prompt=retro+robot'));
app.get('/poster', async (c) => {
try {
const images = await generate('text-to-image', { prompt: c.req.query('prompt') ?? 'a retro robot poster' });
return c.json({ images });
} catch (error) {
return c.json({ error: (error as Error).message }, 502);
}
});NestJS
Înlocuiește /src/app.service.ts și /src/app.controller.ts — POST /summarize cu { "text": "…" } (text-to-text, 1 token AI per apel):
import { BadGatewayException, Injectable } from '@nestjs/common';
import { generate } from './dojocode-ai';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello, World! Try POST /summarize';
}
async summarize(text: string): Promise<{ summary: unknown }> {
try {
const summary = await generate('text-to-text', {
prompt: `Summarize in three bullet points:\n\n${text}`,
max_tokens: 250
});
return { summary };
} catch (error) {
throw new BadGatewayException((error as Error).message);
}
}
}import { Body, Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Post('summarize')
summarize(@Body('text') text: string) {
return this.appService.summarize(text);
}
}Șabloane Node.js
Șabloanele Node.js citesc aceleași variabile process.env indiferent dacă proiectul rulează în previzualizare sau cu Run (unde o rulare se oprește după 20 de secunde — vezi Python — Run pentru modalități lente).
Node.js
Înlocuiește /main.js (text-to-text, 1 token AI):
async function main() {
const response = await fetch(`${process.env.DOJOCODE_API_URL}/ai-generation/generations`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.DOJOCODE_AI_TOKEN}` },
body: JSON.stringify({
modality: 'text-to-text',
input: { prompt: 'Write a one-sentence fun fact about Node.js.', max_tokens: 80 }
}),
signal: AbortSignal.timeout(15000)
});
const record = await response.json();
if (!response.ok) {
console.log('Generation failed: ' + record.errors[0].message);
return;
}
console.log(record.status, String(record.output).trim());
}
main().catch((error) => console.log('Generation failed: ' + error.message));Node.js TS
Înlocuiește /main.ts (text-to-text, 1 token AI):
interface GenerationRecord {
status?: string;
output?: unknown;
errors?: Array<{ message: string }>;
}
async function main(): Promise<void> {
const response = await fetch(`${process.env.DOJOCODE_API_URL}/ai-generation/generations`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.DOJOCODE_AI_TOKEN}` },
body: JSON.stringify({
modality: 'text-to-text',
input: { prompt: 'Write a one-sentence fun fact about TypeScript.', max_tokens: 80 }
}),
signal: AbortSignal.timeout(15000)
});
const record = (await response.json()) as GenerationRecord;
if (!response.ok) {
console.log('Generation failed: ' + record.errors?.[0]?.message);
return;
}
console.log(record.status, String(record.output).trim());
}
main().catch((error: Error) => console.log('Generation failed: ' + error.message));Python
Python — previzualizare în browser
Proiectele Python cu previzualizare în browser rulează în browser, iar platforma instalează un modul predefinit dojocode_ai — nu este necesar cod HTTP. Creează /index.html (pagina pe care o afișează previzualizarea) și înlocuiește /main.py (care conectează pagina) — un generator de cărți poștale (text-to-image, 3 tokenuri AI):
from js import document
from pyodide.ffi import create_proxy
from dojocode_ai import generate_async, DojoCodeAiError
async def on_generate(event):
result = document.getElementById("result")
result.textContent = "Painting your postcard..."
try:
# Waits until the generation finishes; the page stays responsive meanwhile.
record = await generate_async(
"text-to-image",
{"prompt": f"A vintage postcard of {document.getElementById('place').value}", "aspect_ratio": "3:2"},
timeout=150,
)
result.innerHTML = f'<img src="{record["output"][0]}" style="max-width: 100%; border-radius: 8px">'
except DojoCodeAiError as error:
result.textContent = f"Generation failed: {error}"
document.getElementById("generate").addEventListener("click", create_proxy(on_generate))<main style="max-width: 640px; margin: 2rem auto; font-family: sans-serif">
<h1>Postcard generator</h1>
<input id="place" placeholder="Lisbon at sunset" />
<button id="generate">Generate (3 AI tokens)</button>
<div id="result"></div>
</main>Folosește await generate_async(…) / await get_async(id) în gestionarii de evenimente. Funcțiile sincrone generate() / get() există pentru scripturi simple, dar acestea blochează pagina până când sosește răspunsul.
Python — Run
Fără previzualizarea în browser, Run execută /main.py într-un container cu DOJOCODE_API_URL și DOJOCODE_AI_TOKEN în mediu. O rulare se oprește după 20 de secunde, așa că acest exemplu trimite solicitarea fără a aștepta, verifică periodic timp de până la ~10 secunde și afișează ID-ul dacă rezultatul nu este gata încă:
import json, os, time, urllib.request, urllib.error
API_URL = os.environ["DOJOCODE_API_URL"]
HEADERS = {"Content-Type": "application/json", "Authorization": f"Bearer {os.environ['DOJOCODE_AI_TOKEN']}"}
def api(method, path, body=None):
request = urllib.request.Request(f"{API_URL}{path}", method=method, headers=HEADERS,
data=json.dumps(body).encode() if body is not None else None)
try:
with urllib.request.urlopen(request, timeout=15) as response:
return json.load(response)
except urllib.error.HTTPError as error:
raise RuntimeError(json.load(error)["errors"][0]["message"]) from None
record = api("POST", "/ai-generation/generations?wait=false",
{"modality": "text-to-image", "input": {"prompt": "a watercolor fox in the snow"}})
for _ in range(5):
if record["status"] in ("succeeded", "failed", "cancelled"):
break
time.sleep(2)
record = api("GET", f"/ai-generation/generations/{record['id']}")
if record["status"] == "succeeded":
print("Image:", record["output"][0])
else:
print(f"Still {record['status']} — run again to read generation {record['id']}")
# Next run: api("GET", "/ai-generation/generations?limit=1") returns your latest generation.Șabloane de rulare
Aceste șabloane îți rulează programul într-un container când apeși pe Run, cu DOJOCODE_API_URL, DOJOCODE_AI_TOKEN și DOJOCODE_PROJECT_ID în mediu. Exemplele folosesc text-to-text, care se termină cu mult înainte de limita de 20 de secunde; pentru modalități mai lente, urmează modelul Python — Run (trimite cu ?wait=false, afișează ID-ul, citește-l din nou la următoarea rulare).
Java
Înlocuiește /Main.java (text-to-text, 1 token AI):
package challenge;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Main {
public static void main(String[] args) throws Exception {
String apiUrl = System.getenv("DOJOCODE_API_URL");
String token = System.getenv("DOJOCODE_AI_TOKEN");
// Use HTTP/1.1: Java's default HTTP/2 upgrade over plain http is not supported.
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
String body = "{\"modality\":\"text-to-text\",\"input\":{\"prompt\":\"Write a one-sentence fun fact about Java.\",\"max_tokens\":80}}";
HttpRequest request = HttpRequest.newBuilder(URI.create(apiUrl + "/ai-generation/generations"))
.timeout(Duration.ofSeconds(15))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("HTTP " + response.statusCode());
System.out.println(response.body()); // the generation record as JSON
}
}Go
Înlocuiește /main.go (text-to-text, 1 token AI):
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
func main() {
payload, _ := json.Marshal(map[string]interface{}{
"modality": "text-to-text",
"input": map[string]interface{}{"prompt": "Write a one-sentence fun fact about Go.", "max_tokens": 80},
})
request, _ := http.NewRequest("POST", os.Getenv("DOJOCODE_API_URL")+"/ai-generation/generations", bytes.NewReader(payload))
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", "Bearer "+os.Getenv("DOJOCODE_AI_TOKEN"))
client := &http.Client{Timeout: 15 * time.Second}
response, err := client.Do(request)
if err != nil {
fmt.Println("Generation failed:", err)
return
}
defer response.Body.Close()
var record map[string]interface{}
json.NewDecoder(response.Body).Decode(&record)
if failures, ok := record["errors"].([]interface{}); ok && len(failures) > 0 {
fmt.Println("Generation failed:", failures[0].(map[string]interface{})["message"])
return
}
fmt.Println(record["status"], record["output"])
}C#
Înlocuiește /Main.cs (text-to-text, 1 token AI):
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace Challenge
{
public class Program
{
public static void Main(string[] args)
{
var apiUrl = Environment.GetEnvironmentVariable("DOJOCODE_API_URL");
var token = Environment.GetEnvironmentVariable("DOJOCODE_AI_TOKEN");
var payload = JsonSerializer.Serialize(new
{
modality = "text-to-text",
input = new { prompt = "Write a one-sentence fun fact about C#.", max_tokens = 80 }
});
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(15) };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = client.PostAsync(apiUrl + "/ai-generation/generations", content).Result;
using var document = JsonDocument.Parse(response.Content.ReadAsStringAsync().Result);
var record = document.RootElement;
if (record.TryGetProperty("errors", out var errors))
{
Console.WriteLine("Generation failed: " + errors[0].GetProperty("message").GetString());
return;
}
Console.WriteLine(record.GetProperty("status").GetString() + ": " + record.GetProperty("output").GetString()?.Trim());
}
}
}PHP
Înlocuiește /main.php (text-to-text, 1 token AI):
<?php
$apiUrl = getenv('DOJOCODE_API_URL');
$token = getenv('DOJOCODE_AI_TOKEN');
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAuthorization: Bearer $token\r\n",
'content' => json_encode([
'modality' => 'text-to-text',
'input' => ['prompt' => 'Write a one-sentence fun fact about PHP.', 'max_tokens' => 80],
]),
'timeout' => 15,
'ignore_errors' => true,
]]);
$record = json_decode(file_get_contents("$apiUrl/ai-generation/generations", false, $context) ?: 'null', true);
if (!is_array($record) || isset($record['errors'])) {
echo 'Generation failed: ' . ($record['errors'][0]['message'] ?? 'no response') . "\n";
exit;
}
echo "{$record['status']}: " . trim((string) $record['output']) . "\n";Ruby
Înlocuiește /main.rb (text-to-text, 1 token AI):
require 'json'
require 'net/http'
require 'uri'
uri = URI("#{ENV['DOJOCODE_API_URL']}/ai-generation/generations")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.read_timeout = 15
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json',
'Authorization' => "Bearer #{ENV['DOJOCODE_AI_TOKEN']}")
request.body = { modality: 'text-to-text', input: { prompt: 'Write a one-sentence fun fact about Ruby.', max_tokens: 80 } }.to_json
record = JSON.parse(http.request(request).body)
if record['errors']
puts "Generation failed: #{record['errors'][0]['message']}"
else
puts "#{record['status']}: #{record['output'].to_s.strip}"
endRust
Înlocuiește /main.rs (text-to-text, 1 token AI). Biblioteca standard nu are un client HTTP, așa că acest exemplu apelează comanda curl, pe care mediul de rulare Rust Run o include; pentru o aplicație mai mare adaugă un crate precum ureq sau reqwest (plus serde_json) din panoul Dependencies:
use std::env;
use std::process::Command;
fn main() {
let api_url = env::var("DOJOCODE_API_URL").unwrap_or_default();
let token = env::var("DOJOCODE_AI_TOKEN").unwrap_or_default();
let payload = r#"{"modality":"text-to-text","input":{"prompt":"Write a one-sentence fun fact about Rust.","max_tokens":80}}"#;
let output = Command::new("curl")
.args([
"-s", "--max-time", "15", "-X", "POST",
"-H", "Content-Type: application/json",
"-H", &format!("Authorization: Bearer {}", token),
"-d", payload,
&format!("{}/ai-generation/generations", api_url),
])
.output()
.expect("curl is not available");
// The generation record as JSON — parse it with serde_json in a real app.
println!("{}", String::from_utf8_lossy(&output.stdout));
}Ce urmează?
- 📘 Acces la modelele AI — referința completă: prețuri, endpoint-uri, intrări pentru fiecare modalitate, limite și depanare
- 🧩 Șabloane de proiect
- 🧪 Testarea rutelor de server cu API Tester