UNPKG

@devvai/devv-code-backend

Version:

Backend SDK for Devv Code - Provides authentication, data management, email and AI capabilities

667 lines (539 loc) 23.3 kB
# Devv Code Backend SDK Internal SDK providing authentication and data storage functionality for Devv Code frontend projects. ## Authentication Requirements For data security, most operations require authentication via `auth.verifyOTP()`: - **Authentication required**: table write operations (add/update/delete), file upload, email sending, AI chat - **No authentication needed**: table read operations (`getItems`) Always ensure users are logged in before performing write operations or using AI/email features. ## Installation ```bash npm install @devvai/devv-code-backend ``` ## Import ```typescript import { auth, table, upload, email, imageGen, webSearch, webReader, replicate } from '@devvai/devv-code-backend'; import { DevvAI, OpenRouterAI, DevvImageGen, DevvWebSearch, DevvWebReader, DevvReplicate } from '@devvai/devv-code-backend'; // Import types for TypeScript users import type { ReplicateTextToImageModel } from '@devvai/devv-code-backend'; ``` ## Authentication API ### auth.sendOTP(email: string): Promise<void> Sends a verification code to the user's email. ### auth.verifyOTP(email: string, verificationCode: string): Promise<AuthResponse> Verifies the email verification code and logs in, returning user information and session. ### auth.logout(): Promise<void> Logs out and clears the session. ## Data Table API ### table.addItem(tableId: string, data: Record<string, any>): Promise<void> Adds a data item to the specified table. ### table.getItems(tableId: string, options?: GetItemsOptions): Promise<GetItemsResponse> Queries table data with support for filtering, sorting, and pagination. Returns all data using the default `_tid_id_idx` index when no parameters are provided. ### table.updateItem(tableId: string, data: ItemData): Promise<void> Updates a data item. The data must include complete primary keys. ### table.deleteItem(tableId: string, keys: ItemData): Promise<void> Deletes a data item. The keys must include complete primary keys. ## File Upload API ### upload.uploadFile(file: File): Promise<UploadFileResponse> Uploads a file to the server. Returns the upload result including filename and link. ### upload.isErrorResponse(response: UploadFileResponse): response is ErrorResponse Type guard to check if the response is an error response. ## Email API ### email.sendEmail(options: SendEmailOptions): Promise<EmailResponse> Sends an email through the Resend service. Supports HTML/text content, attachments, scheduling, and more. ## Image Generation API ### imageGen.textToImage(options: TextToImageOptions): Promise<TextToImageResponse> Generates images from text prompts using AI models. Supports both text-to-image and image-to-image transformations. **Parameters:** - `prompt` (required): Text description of the image to generate or transformation to apply - `model` (optional): AI model to use (e.g., 'google/gemini-2.5-flash-image') - `image_url` (optional): URL of existing image for image-to-image transformation (works with google/gemini-2.5-flash-image) - `num_outputs` (optional): Number of images to generate (1-4, default: 1) - `aspect_ratio` (optional): Image aspect ratio (e.g., '16:9', '1:1', '9:16') - `output_format` (optional): Output format ('webp', 'jpg', 'png') **Returns:** - `images`: Array of generated image URLs ## Web Search API ### webSearch.search(options: WebSearchOptions): Promise<WebSearchResponse> Search the web using Jina's SERP API to get search results in LLM-friendly format. ## Web Reader API ### webReader.read(options: WebReaderOptions): Promise<WebReaderResponse> Extract clean, LLM-friendly content from any web page using Jina's Reader API. ## Replicate API ### replicate.textToImage(options: ReplicateImageOptions): Promise<ReplicateImageResponse> Generates high-quality images using pre-approved Replicate models. **Supported Models:** - `ideogram-ai/ideogram-v3-turbo` - High-quality creative generation - `black-forest-labs/flux-schnell` - Fast, efficient generation - `google/imagen-4-fast` - Google's fast model - `black-forest-labs/flux-kontext-pro` - Context-aware generation - `prunaai/hidream-l1-fast` - Optimized fast model - `luma/photon-flash` - Ultra-fast photorealistic **Parameters:** - `prompt` (required): Text description - `model` (required): One of the supported models above - `num_outputs`: Number of images (1-4, default: 1) - `negative_prompt`: Things to avoid in generation - `guidance_scale`: Prompt adherence strength - Plus other model-specific parameters ## AI API ### DevvAI Pre-configured OpenAI client (v5.10.2) with automatic authentication. Create an instance after user login to ensure proper authentication. **Important**: - No parameters needed when creating `new DevvAI()` - all authentication and configuration is handled internally for security - The `model` parameter is required in API calls but will be overridden by Devv backend based on your project configuration ```typescript // Create AI client after login - no parameters needed const ai = new DevvAI(); // Example usage const response = await ai.chat.completions.create({ model: 'gpt-3.5-turbo', // Required but overridden by backend messages: [{ role: 'user', content: 'Hello!' }] }); // Streaming const stream = await ai.chat.completions.create({ model: 'gpt-3.5-turbo', messages: [{ role: 'user', content: 'Tell me a story' }], stream: true }); ``` For complete usage documentation, refer to the official OpenAI SDK: https://github.com/openai/openai-node ## OpenRouter AI API ### OpenRouterAI Alternative AI client that uses OpenRouter service for access to multiple model providers. Requires external OpenRouter API key configuration. **Important**: - Requires OpenRouter API key to be configured externally in your project settings - Model parameter must use OpenRouter format: `provider/model-name` - If you encounter API key errors, ensure you have configured your OpenRouter API key in the external API keys configuration ```typescript // Create OpenRouter AI client after login const ai = new OpenRouterAI(); // Using OpenAI models const response = await ai.chat.completions.create({ model: 'openai/gpt-4-turbo', // Required: specify OpenRouter model format messages: [{ role: 'user', content: 'Hello!' }] }); // Using Claude models const claudeResponse = await ai.chat.completions.create({ model: 'anthropic/claude-3-opus', messages: [{ role: 'user', content: 'Tell me a joke' }] }); // Using other providers const geminiResponse = await ai.chat.completions.create({ model: 'google/gemini-pro', messages: [{ role: 'user', content: 'Explain quantum computing' }] }); // Streaming also supported const stream = await ai.chat.completions.create({ model: 'meta-llama/llama-3-70b-instruct', messages: [{ role: 'user', content: 'Write a poem' }], stream: true }); ``` **Common OpenRouter Models**: - OpenAI: `openai/gpt-4-turbo`, `openai/gpt-3.5-turbo` - Anthropic: `anthropic/claude-3-opus`, `anthropic/claude-3-sonnet` - Google: `google/gemini-pro`, `google/gemini-pro-vision` - Meta: `meta-llama/llama-3-70b-instruct`, `meta-llama/llama-3-8b-instruct` - And many more providers... ## Text-to-Speech API ### tts.convert(options: TextToSpeechOptions): Promise<TextToSpeechResponse> Converts text to speech using ElevenLabs API service. **Important**: - Requires ElevenLabs API key to be configured externally in your project settings - Uses ElevenLabs Text-to-Speech API v1 - For detailed parameter documentation, see: https://elevenlabs.io/docs/api-reference/text-to-speech ```typescript // Simple usage - only text is required const result = await tts.convert({ text: "Hello, world!" }); console.log(result.audio_url); // URL to generated audio file console.log(result.format); // e.g., "mp3_44100_128" console.log(result.size); // File size in bytes // Optional parameters available: // - voice_id: ElevenLabs voice ID // - model_id: ElevenLabs model ID // - output_format: Audio format // - stability: Voice stability (0-1) // - similarity_boost: Voice similarity (0-1) // - style: Voice style (0-1) ``` ## Type Definitions ```typescript interface User { projectId: string; uid: string; name: string; email: string; createdTime: number; lastLoginTime: number; } interface AuthResponse { sid: string; user: User; } interface GetItemsOptions { limit?: number; // Items per page, max 100, default 20 cursor?: string; // Pagination cursor (from previous response's nextCursor) sort?: string; // Sort field (must be range key of current index) order?: 'asc' | 'desc'; // Sort order, default 'asc' query?: Record<string, string | number | boolean | QueryCondition>; // Query conditions, fields must be indexed } interface QueryCondition { operator: QueryOperator; value: QueryValue; } type QueryOperator = 'EQ' | 'NE' | 'LT' | 'LE' | 'GT' | 'GE' | 'BEGINS_WITH' | 'BETWEEN'; type QueryValue = string | number | boolean | [string | number, string | number]; interface GetItemsResponse { items: Record<string, any>[]; nextCursor?: string; indexName?: string; } type ItemData = Record<string, any>; interface UploadFileSuccessResponse { [key: string]: string | undefined; filename?: string; link?: string; } interface ErrorResponse { errCode: number; errMsg: string; } type UploadFileResponse = UploadFileSuccessResponse | ErrorResponse; interface SendEmailOptions { from: string; // Sender email (must be verified) to: string[]; // Array of recipient emails subject: string; // Email subject bcc?: string[]; // BCC recipients cc?: string[]; // CC recipients reply_to?: string; // Reply-to address html?: string; // HTML content text?: string; // Plain text content (at least one of html/text required) tags?: EmailTag[]; // Metadata tags attachments?: EmailAttachment[]; // File attachments headers?: Record<string, string>; // Custom headers scheduled_at?: string; // ISO 8601 timestamp for scheduled delivery } interface EmailTag { name: string; value: string; } interface EmailAttachment { filename: string; // Filename with extension content?: number[]; // Binary content as byte array (0-255) path?: string; // URL to attachment (either content or path required) content_type?: string; // MIME type } interface EmailResponse { id: string; // Email ID for tracking } type ImageAspectRatio = '1:1' | '16:9' | '21:9' | '3:2' | '2:3' | '4:5' | '5:4' | '3:4' | '4:3' | '9:16' | '9:21'; type ImageOutputFormat = 'webp' | 'jpg' | 'png'; interface TextToImageOptions { prompt: string; // Text description for image generation model?: string; // Optional model (e.g., 'google/gemini-2.5-flash-image') image_url?: string; // Optional: URL for image-to-image transformation num_outputs?: number; // Number of images (1-4, default: 1) aspect_ratio?: ImageAspectRatio; // Image aspect ratio output_format?: ImageOutputFormat; // Output format } interface TextToImageResponse { images: string[]; // Array of generated image URLs } // Web Search Types interface WebSearchOptions { query: string | string[]; // Search query } interface WebSearchResult { title: string; // Page title url: string; // Page URL description: string; // Page description content: string; // Extracted content date?: string; // Published date } interface WebSearchResponse { code: number; // Response code (200 for success) status: number; // Status code (20000 for success) data: WebSearchResult[]; // Array of search results meta?: { usage?: { tokens: number; // Token usage }; }; } // Web Reader Types interface WebReaderOptions { url: string; // URL to read } interface WebReaderData { title: string; // Page title description: string; // Meta description url: string; // Original URL content: string; // Clean extracted content publishedTime?: string; // Publication timestamp metadata?: Record<string, any>; // Page metadata external?: Record<string, any>; // External resources warning?: string; // Cache warning } interface WebReaderResponse { code: number; // Response code (200 for success) status: number; // Status code (20000 for success) data: WebReaderData; // Extracted page data meta?: { usage?: { tokens: number; // Token usage }; }; } ``` ## Query Operators | Operator | Description | Applies To | |----------|-------------|------------| | EQ | Equals (default) | Hash key (required), Range key | | NE | Not equals | Range key only | | LT | Less than | Range key only | | LE | Less than or equal | Range key only | | GT | Greater than | Range key only | | GE | Greater than or equal | Range key only | | BEGINS_WITH | Prefix match | Range key only | | BETWEEN | Range (value must be two-element array) | Range key only | ## Internal Session Management The SDK automatically handles device identification and session management, completely transparent to users: ### Device ID - **Storage Location**: `localStorage.getItem('DEVV_CODE_DEVICE_ID')` - **Generation Time**: Automatically generated during SDK initialization (if not exists) - **Handling**: - Automatically generates UUID v4 format device ID - Sent after double SHA256 encryption - Automatically included in all API request headers as `Device-Id` field ### Session ID - **Storage Location**: `localStorage.getItem('DEVV_CODE_SID')` - **Generation Time**: Automatically saved after successful `verifyOTP` call - **Clear Time**: Automatically cleared when calling `logout` - **Handling**: - Automatically extracted and saved from response after successful login - Automatically included in headers of all authenticated API requests as `sid` field - No manual session state management needed ## File Upload Usage Example ```typescript import { upload } from '@devvai/devv-code-backend'; // Upload a file async function handleFileUpload(file: File) { try { const result = await upload.uploadFile(file); if (upload.isErrorResponse(result)) { console.error(`Upload failed: ${result.errMsg}`); if (result.errCode === 429) { console.error('Upload limit exceeded: max 200 uploads per 24 hours'); } } else { console.log('Upload successful:', result); console.log('File link:', result.link); } } catch (error) { console.error('Upload error:', error); } } // Example with file input const fileInput = document.querySelector<HTMLInputElement>('#file-input'); fileInput?.addEventListener('change', (event) => { const file = (event.target as HTMLInputElement).files?.[0]; if (file) { handleFileUpload(file); } }); ``` ## Image Generation Usage Example ```typescript import { imageGen } from '@devvai/devv-code-backend'; // Generate a single image async function generateImage(prompt: string) { try { const result = await imageGen.textToImage({ prompt: prompt }); console.log('Generated image:', result.images[0]); } catch (error) { console.error('Failed to generate image:', error); } } // Generate multiple images with custom settings async function generateMultipleImages(prompt: string) { try { const result = await imageGen.textToImage({ prompt: prompt, num_outputs: 3, // Generate 3 images aspect_ratio: '16:9', // Widescreen format output_format: 'png' // PNG format }); console.log('Generated images:', result.images); } catch (error) { console.error('Failed to generate images:', error); } } // Generate portrait image async function generatePortrait(description: string) { try { const result = await imageGen.textToImage({ prompt: `portrait of ${description}`, aspect_ratio: '3:4', // Portrait orientation output_format: 'jpg' }); return result.images[0]; } catch (error) { console.error('Image generation failed:', error); throw error; } } // Image-to-image transformation with Google Gemini model async function transformImage(imageUrl: string, transformation: string) { try { const result = await imageGen.textToImage({ prompt: transformation, // e.g., "update the text to black" model: 'google/gemini-2.5-flash-image', image_url: imageUrl, // URL of the image to transform output_format: 'png' }); console.log('Transformed image:', result.images[0]); return result.images[0]; } catch (error) { console.error('Image transformation failed:', error); throw error; } } ``` ## Email Usage Example ```typescript import { email } from '@devvai/devv-code-backend'; // Send a simple text email async function sendWelcomeEmail(userEmail: string) { try { const result = await email.sendEmail({ from: 'noreply@yourdomain.com', to: [userEmail], subject: 'Welcome to Our Service', text: 'Thank you for signing up!' }); console.log('Email sent:', result.id); } catch (error) { console.error('Failed to send email:', error); } } // Send HTML email with attachment async function sendInvoiceEmail(customerEmail: string, invoiceUrl: string) { try { const result = await email.sendEmail({ from: 'billing@yourdomain.com', to: [customerEmail], subject: 'Your Invoice', html: '<h1>Invoice</h1><p>Please find your invoice attached.</p>', text: 'Invoice\n\nPlease find your invoice attached.', attachments: [{ filename: 'invoice.pdf', path: invoiceUrl, content_type: 'application/pdf' }], tags: [ { name: 'type', value: 'invoice' }, { name: 'customer', value: customerEmail } ] }); console.log('Invoice email sent:', result.id); } catch (error) { console.error('Failed to send invoice:', error); } } // Schedule an email async function scheduleReminder(userEmail: string, appointmentDate: Date) { const reminderDate = new Date(appointmentDate); reminderDate.setDate(reminderDate.getDate() - 1); // Day before appointment try { const result = await email.sendEmail({ from: 'reminders@yourdomain.com', to: [userEmail], subject: 'Appointment Reminder', text: `Reminder: You have an appointment tomorrow at ${appointmentDate.toLocaleTimeString()}.`, scheduled_at: reminderDate.toISOString() }); console.log('Reminder scheduled:', result.id); } catch (error) { console.error('Failed to schedule reminder:', error); } } ``` ## Web Search Usage Example ```typescript import { webSearch } from '@devvai/devv-code-backend'; // Simple web search const result = await webSearch.search({ query: "artificial intelligence news" }); // Access results result.data.forEach(item => { console.log(item.title, item.url); console.log(item.description); }); ``` ## Web Reader Usage Example ```typescript import { webReader } from '@devvai/devv-code-backend'; // Read a web page const result = await webReader.read({ url: "https://example.com/article" }); // Access content console.log(result.data.title); console.log(result.data.content); ``` ## Usage Guidelines 1. **Authentication Flow**: First call `sendOTP` to send verification code, then call `verifyOTP` to complete login. It's recommended to use state management libraries (like Zustand) to persist user information. 2. **Query Constraints**: - Returns all data using default `_tid_id_idx` index when no parameters provided - Conditional queries must be based on indexed fields (primary or secondary index) - Primary index is fixed as `_uid` (user ID) and `_id` (time-ordered ID) - Hash keys can only use EQ operator, other operators will cause errors - Range keys can use all operators - Special case: When `_tid` is the hash key of an index, the system fills it automatically, no manual specification needed 3. **Sorting Rules**: - `sort` field must be the range key of the currently used index - Cannot sort by arbitrary fields 4. **Route Protection**: You need to implement authentication guards yourself to check user login status and handle route redirects. 5. **Error Handling**: All methods may throw exceptions, please use try-catch for handling. 6. **Pagination Best Practices**: Use cursor pagination, not offset. Cursors remain valid as long as the data structure doesn't change. 7. **File Upload Limits**: Maximum 200 file uploads per 24 hours per user. Files are automatically handled with proper multipart/form-data encoding. 8. **Email Requirements**: - `from` address must be from a verified domain or email address - At least one of `html` or `text` content is required - For attachments, provide either `content` (byte array) or `path` (URL), not both - Scheduled emails must have a future `scheduled_at` timestamp 9. **AI Chat Usage**: - Requires active session (must login first with `auth.verifyOTP()`) - **DevvAI**: The `model` parameter is required but ignored - Devv backend controls the actual model - **OpenRouterAI**: The `model` parameter must use OpenRouter format (`provider/model-name`) - Full OpenAI SDK compatibility - all features work identically - No additional dependencies needed - OpenAI SDK is included 10. **OpenRouter AI Usage**: - Requires active session (must login first with `auth.verifyOTP()`) - Must have OpenRouter API key configured in external API keys settings - Model parameter uses format: `provider/model-name` (e.g., `openai/gpt-4-turbo`, `anthropic/claude-3-opus`) - If you see API key errors, check your OpenRouter API key configuration - Supports all OpenRouter available models from various providers 11. **Image Generation Usage**: - Requires active session (must login first with `auth.verifyOTP()`) - Uses Flux Schnell model for high-quality text-to-image generation - `num_outputs` must be between 1-4 (default is 1) - Generated images are returned as URLs that can be directly used in your application - Model documentation: https://replicate.com/black-forest-labs/flux-schnell 12. **Web Search and Reader Usage**: - Requires active session (must login first with `auth.verifyOTP()`) - Web Search returns up to 5 most relevant results - Web Reader extracts clean, LLM-friendly content from any URL - Search queries can be strings or arrays of strings - URLs must be properly formatted (include https://) ## Important Notes - Device ID and Session ID are stored in localStorage, clearing browser data will lose them - Queries don't support non-indexed fields, attempting to query non-indexed fields will cause errors - BETWEEN operator value must be a two-element array - The system automatically selects the appropriate index based on your query parameters - Performance consideration: Use indexed fields for queries to achieve optimal performance