Spaces:
Paused
Paused
| # API Module | |
| Axios-based client and resource-specific API modules for backend communication with auth, FormData handling, and error recovery. | |
| ## Key Components | |
| - **`client.ts`**: Central Axios instance with request/response interceptors, auth headers, base URL resolution | |
| - **Resource modules** (`sources.ts`, `notebooks.ts`, `chat.ts`, `search.ts`, `podcasts.ts`, etc.): Endpoint-specific functions returning typed responses | |
| - **`query-client.ts`**: TanStack Query client configuration with default options | |
| - **`models.ts`, `notes.ts`, `embeddings.ts`, `settings.ts`**: Additional resource APIs | |
| ## Important Patterns | |
| - **Single axios instance**: `apiClient` with 10-minute timeout (for slow LLM operations) | |
| - **Request interceptor**: Auto-fetches base URL from config, adds Bearer auth from localStorage `auth-storage` | |
| - **FormData handling**: Auto-removes Content-Type header for FormData to let browser set multipart boundary | |
| - **Response interceptor**: 401 clears auth and redirects to `/login` | |
| - **Async base URL resolution**: `getApiUrl()` fetches from runtime config on first request | |
| - **Error propagation**: All functions return typed responses via `response.data` | |
| - **Method chaining**: Resource modules export namespaced objects (e.g., `sourcesApi.list()`, `sourcesApi.create()`) | |
| ## Key Dependencies | |
| - `axios`: HTTP client library | |
| - `@/lib/config`: `getApiUrl()` for dynamic base URL | |
| - `@/lib/types/api`: TypeScript types for request/response shapes | |
| ## How to Add New API Modules | |
| 1. Create new file (e.g., `transforms.ts`) | |
| 2. Import `apiClient` | |
| 3. Export namespaced object with methods: | |
| ```typescript | |
| export const transformsApi = { | |
| list: async () => { const response = await apiClient.get('/transforms'); return response.data } | |
| } | |
| ``` | |
| 4. Add types to `@/lib/types/api` if new response shapes needed | |
| ## Important Quirks & Gotchas | |
| - **Base URL delay**: First request waits for `getApiUrl()` to resolve; can be slow on startup | |
| - **FormData fields as JSON strings**: Nested objects (arrays, objects) must be JSON stringified in FormData (e.g., `notebooks`, `transformations`) | |
| - **Timeout for streaming**: 10-minute timeout may not cover very long-running LLM operations; consider extending if needed | |
| - **Auth token management**: Token stored in localStorage `auth-storage` key; uses Zustand persist middleware | |
| - **Headers mutation in interceptor**: Mutating `config.headers` directly; be careful with middleware order | |
| - **No automatic retry logic**: Failed requests not automatically retried; must be handled in consuming code. Podcast episodes have explicit retry via `retryEpisode()` in `podcasts.ts` and `useRetryPodcastEpisode()` hook | |
| - **Content-Type header precedence**: FormData interceptor deletes Content-Type after checking; subsequent interceptors won't re-add it | |
| ## Usage Example | |
| ```typescript | |
| // Basic list | |
| const sources = await sourcesApi.list({ notebook_id: notebookId }) | |
| // File upload with FormData | |
| const response = await sourcesApi.create({ | |
| type: 'upload', | |
| file: fileObj, | |
| notebook_id: notebookId, | |
| async_processing: true | |
| }) | |
| // With auth token (auto-added by interceptor) | |
| const notes = await notesApi.list() | |
| ``` | |
| ## Credentials Module (`credentials.ts`) | |
| Client functions for managing AI provider credentials (API keys, base URLs, endpoints) stored encrypted in SurrealDB. | |
| ### Type Definitions | |
| ```typescript | |
| // Full credential object (api_key never exposed) | |
| interface Credential { | |
| id: string | |
| name: string | |
| provider: string | |
| modalities: string[] | |
| has_api_key: boolean | |
| model_count: number | |
| base_url?: string | |
| endpoint?: string | |
| api_version?: string | |
| // ... endpoint_llm, endpoint_embedding, endpoint_stt, endpoint_tts, project, location, credentials_path | |
| } | |
| // Request payload for creating/updating credential | |
| interface CreateCredentialRequest { | |
| name: string | |
| provider: string | |
| modalities: string[] | |
| api_key?: string | |
| base_url?: string | |
| // ... other provider-specific fields | |
| } | |
| // Model discovery and registration | |
| interface DiscoverModelsResponse { provider: string; models: DiscoveredModel[]; credential_id: string } | |
| interface RegisterModelsRequest { models: RegisterModelData[] } | |
| // Status and migration | |
| interface CredentialStatus { configured: Record<string, boolean>; source: Record<string, string>; encryption_configured: boolean } | |
| interface EnvStatus { [provider: string]: boolean } | |
| interface MigrationResult { message: string; migrated: string[]; skipped: string[]; errors: string[] } | |
| interface TestConnectionResult { provider: string; success: boolean; message: string } | |
| ``` | |
| ### API Functions | |
| | Function | Description | Endpoint | | |
| |----------|-------------|----------| | |
| | `getStatus()` | Get configuration status of all providers | `GET /credentials/status` | | |
| | `getEnvStatus()` | Get which providers have env vars set | `GET /credentials/env-status` | | |
| | `list(provider?)` | List all credentials (optional filter) | `GET /credentials` | | |
| | `listByProvider(provider)` | List credentials for a provider | `GET /credentials/by-provider/{provider}` | | |
| | `get(credentialId)` | Get a specific credential | `GET /credentials/{credentialId}` | | |
| | `create(data)` | Create a new credential | `POST /credentials` | | |
| | `update(credentialId, data)` | Update a credential | `PUT /credentials/{credentialId}` | | |
| | `delete(credentialId, options?)` | Delete a credential | `DELETE /credentials/{credentialId}` | | |
| | `test(credentialId)` | Test connection using credential | `POST /credentials/{credentialId}/test` | | |
| | `discover(credentialId)` | Discover available models | `POST /credentials/{credentialId}/discover` | | |
| | `registerModels(credentialId, data)` | Register discovered models | `POST /credentials/{credentialId}/register-models` | | |
| | `migrateFromProviderConfig()` | Migrate from legacy ProviderConfig | `POST /credentials/migrate-from-provider-config` | | |
| | `migrateFromEnv()` | Migrate from env vars | `POST /credentials/migrate-from-env` | | |
| ### Usage Example | |
| ```typescript | |
| import { credentialsApi } from '@/lib/api/credentials' | |
| // Check which providers are configured | |
| const status = await credentialsApi.getStatus() | |
| if (status.configured['openai']) { | |
| console.log(`OpenAI configured via ${status.source['openai']}`) | |
| } | |
| // Create a new credential | |
| const cred = await credentialsApi.create({ | |
| name: 'My OpenAI Key', | |
| provider: 'openai', | |
| modalities: ['language', 'embedding'], | |
| api_key: 'sk-proj-...' | |
| }) | |
| // Test the connection | |
| const result = await credentialsApi.test(cred.id) | |
| if (result.success) { | |
| console.log('Connection successful!') | |
| } | |
| // Discover and register models | |
| const discovered = await credentialsApi.discover(cred.id) | |
| await credentialsApi.registerModels(cred.id, { | |
| models: discovered.models.map(m => ({ model_id: m.model_id, name: m.name, type: 'language' })) | |
| }) | |
| ``` | |