### Start Development Environment Source: https://github.com/xuanzhi33/llm-gate/blob/main/README.md Use this command to start both the frontend and Rust backend for development. Ensure pnpm is installed and the project dependencies are set up. ```bash pnpm tauri dev ``` -------------------------------- ### Install project dependencies Source: https://github.com/xuanzhi33/llm-gate/blob/main/README.md Command to install project dependencies using pnpm. This is typically run before starting local development. ```bash pnpm install ``` -------------------------------- ### Start LLM Gate Development Server Source: https://github.com/xuanzhi33/llm-gate/blob/main/AGENTS.md Starts both the Vite development server for the frontend and the Tauri application for the backend. DevTools open automatically in debug builds. Frontend auto-reloads on changes; Rust requires a full restart. ```bash pnpm tauri dev # Starts both Vite dev server and Tauri ``` -------------------------------- ### startProxyServer / stopProxyServer — Low-Level Tauri Commands Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Directly invoke the Rust-side Tauri commands to start and stop the Axum HTTP server. These are thin wrappers used internally by `useProxyStore`. ```APIDOC ## `startProxyServer` / `stopProxyServer` — Low-Level Tauri Commands Utility functions that directly invoke the Rust-side Tauri commands to manage the Axum HTTP server. ### `startProxyServer(options: ProxyOptions)` Starts the Axum HTTP server on a specific interface and port. - **`options`** (ProxyOptions) - Required - Configuration for the proxy server. - **`host`** (string) - The interface to bind to (e.g., '127.0.0.1'). - **`port`** (number) - The port to listen on. ### `stopProxyServer()` Stops the running Axum HTTP server. ### Example Usage ```typescript import { startProxyServer, stopProxyServer } from '@/utils/proxy' import type { ProxyOptions } from '@/utils/proxy' const options: ProxyOptions = { host: '127.0.0.1', port: 11456 } await startProxyServer(options) // ... later ... await stopProxyServer() ``` ``` -------------------------------- ### useProxyStore - Proxy Server State & Control Source: https://context7.com/xuanzhi33/llm-gate/llms.txt This section describes the Pinia store `useProxyStore` used for managing the local gateway server's lifecycle and state within the application. It details how to initialize listeners, start, stop, and restart the server, and access its reactive state. ```APIDOC ## `useProxyStore` — Proxy Server State & Control Pinia store (`src/stores/proxy.ts`) that manages the lifecycle of the local gateway server. It listens to Tauri IPC events (`proxy-status-change`, `proxy-request`) emitted by the Rust backend and exposes reactive state alongside `start`, `stop`, and `restart` actions. The store calls the Tauri commands `start_proxy_server` and `stop_proxy_server` via `invoke`. ### Initialization and Event Listeners ```typescript import { useProxyStore } from '@/stores/proxy' import { useSettingsStore } from '@/stores/settings' const proxyStore = useProxyStore() const settingsStore = useSettingsStore() // Initialize event listeners (call once on app mount) await proxyStore.initListeners() // Subscribes to 'proxy-status-change' → updates status/host/port/requestCount // Subscribes to 'proxy-request' → increments requestCount, logs forwarded requests ``` ### Server Control Actions ```typescript // Start the server (reads host/port from settingsStore) await proxyStore.start() // If already running, automatically calls restart() // Stop the server await proxyStore.stop() // Restart (stop then start) await proxyStore.restart() ``` ### Reactive State Access ```typescript // Check reactive state console.log(proxyStore.status) // 'running' | 'stopped' console.log(proxyStore.host) // '127.0.0.1' or '0.0.0.0' console.log(proxyStore.port) // e.g. 11456 console.log(proxyStore.requestCount) // number of proxied requests since last start ``` ### Toggle Helper Example ```typescript // Toggle helper (used by ServiceStatusCard) async function toggleProxy() { if (proxyStore.status === 'running') { await proxyStore.stop() } else { await proxyStore.start() } } ``` ``` -------------------------------- ### Start and Stop Proxy Server Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Directly invoke Rust-side Tauri commands to manage the Axum HTTP server. Prefer using the `useProxyStore` for reactive state management in Vue components. ```typescript import { startProxyServer, stopProxyServer } from '@/utils/proxy' import type { ProxyOptions } from '@/utils/proxy' // Start the Axum HTTP server on a specific interface and port const options: ProxyOptions = { host: '127.0.0.1', port: 11456 } await startProxyServer(options) // Stop the running server await stopProxyServer() ``` -------------------------------- ### Manage App Updates with `useUpdateStore` Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Use this Pinia store to handle the complete application update flow, including checking for new releases, downloading, installing, and relaunching. It provides reactive states for download progress and error messages. ```typescript import { useUpdateStore } from '@/stores/update' const updateStore = useUpdateStore() // Check for a new release (called automatically on app mount) const hasUpdate = await updateStore.checkUpdate() // If update found: updateAvailable = true, showDialog = true // Reactive download progress state console.log(updateStore.checking) // boolean console.log(updateStore.updateAvailable) // boolean console.log(updateStore.updateInfo) // Tauri Update object | null console.log(updateStore.downloading) // boolean console.log(updateStore.downloadProgress) // 0–100 (percentage) console.log(updateStore.downloadedBytes) // bytes received console.log(updateStore.downloadTotal) // total bytes console.log(updateStore.installing) // true after download completes console.log(updateStore.errorMsg) // string | null on failure // Download, install, and relaunch — driven by await updateStore.startDownload() // Internally calls updateInfo.downloadAndInstall(progressCallback) // then relaunch() after Finished event // Dismiss the update dialog without installing updateStore.dismiss() ``` -------------------------------- ### useUpdateStore — Auto-Update Lifecycle Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Manages the complete update flow using @tauri-apps/plugin-updater. It checks for updates, shows a dialog, downloads with progress tracking, installs, and relaunches the application. ```APIDOC ## `useUpdateStore` — Auto-Update Lifecycle Manages the complete update flow using `@tauri-apps/plugin-updater`. Checks for updates against the GitHub Releases endpoint, shows a dialog, downloads with progress tracking, installs, and relaunches. ### Methods - **`checkUpdate()`**: Checks for a new release. Returns a boolean indicating if an update is available. - **`startDownload()`**: Downloads, installs, and relaunches the application. - **`dismiss()`**: Dismisses the update dialog without installing. ### State Properties - **`checking`** (boolean): Indicates if an update check is in progress. - **`updateAvailable`** (boolean): Indicates if an update is available. - **`updateInfo`** (Tauri Update object | null): Information about the available update. - **`downloading`** (boolean): Indicates if an update is currently being downloaded. - **`downloadProgress`** (number): The download progress percentage (0-100). - **`downloadedBytes`** (number): The number of bytes downloaded. - **`downloadTotal`** (number): The total size of the download in bytes. - **`installing`** (boolean): Indicates if the update is being installed. - **`errorMsg`** (string | null): An error message if the update process fails. ### Example Usage ```typescript import { useUpdateStore } from '@/stores/update' const updateStore = useUpdateStore() // Check for a new release (called automatically on app mount) const hasUpdate = await updateStore.checkUpdate() // Download, install, and relaunch await updateStore.startDownload() // Dismiss the update dialog updateStore.dismiss() ``` ``` -------------------------------- ### Chat Completions Endpoint Source: https://github.com/xuanzhi33/llm-gate/blob/main/README.md This section details how to interact with the chat completions endpoint of LLM Gate. It shows the expected URL format and provides examples using curl and the OpenAI Python library. ```APIDOC ## POST /v1/chat/completions ### Description Proxies chat completion requests to a configured LLM. ### Method POST ### Endpoint `http://{host}:{port}/{model_id}/v1/chat/completions` ### Parameters #### Path Parameters - **host** (string) - Required - The hostname of the LLM Gate proxy (e.g., `localhost`). - **port** (integer) - Required - The port the LLM Gate proxy is running on (default: `11456`). - **model_id** (string) - Required - The unique identifier for the configured model. #### Request Body - **model** (string) - Required - The name of the model to use (e.g., `gpt-4`). This can be overridden by LLM Gate if a specific 'Model Name' was configured. - **messages** (array) - Required - An array of message objects representing the conversation history. - **role** (string) - Required - The role of the message sender (e.g., `user`, `assistant`, `system`). - **content** (string) - Required - The content of the message. ### Request Example (curl) ```bash curl http://localhost:11456/my-gpt4/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer dummy-key" \ -d '{ "model": "gpt-4", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ### Request Example (OpenAI Python Library) ```python from openai import OpenAI client = OpenAI( base_url="http://localhost:11456/my-gpt4/v1", api_key="dummy-key" # LLM Gate will inject the real key ) response = client.chat.completions.create( model="gpt-4", # This can be overridden by LLM Gate's 'Model Name' setting messages=[ {"role": "user", "content": "Hello, please introduce yourself."} ] ) print(response.choices[0].message.content) ``` ### Response #### Success Response (200) - **id** (string) - Unique identifier for the completion. - **object** (string) - Type of object returned (e.g., `chat.completion`). - **created** (integer) - Unix timestamp of when the completion was created. - **model** (string) - The model used for the completion. - **choices** (array) - An array of completion choices. - **index** (integer) - Index of the choice. - **message** (object) - The message content. - **role** (string) - The role of the message sender (e.g., `assistant`). - **content** (string) - The content of the assistant's reply. - **finish_reason** (string) - The reason the model stopped generating tokens (e.g., `stop`, `length`). - **usage** (object) - Usage statistics for the completion. - **prompt_tokens** (integer) - Number of tokens in the prompt. - **completion_tokens** (integer) - Number of tokens in the completion. - **total_tokens** (integer) - Total number of tokens used. ``` -------------------------------- ### Use curl to call LLM Gate API Source: https://github.com/xuanzhi33/llm-gate/blob/main/README.md Example of how to use curl to send a chat completion request to the LLM Gate proxy. Ensure the proxy is running and configured with the specified model ID. ```bash curl http://localhost:11456/my-gpt4/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer dummy-key" \ -d '{ "model": "gpt-4", "messages": [{"role": "user", "content": "Hello!"}] }' ``` -------------------------------- ### Proxy API Endpoint Source: https://context7.com/xuanzhi33/llm-gate/llms.txt This section details how to interact with the LLM Gate's proxy API, which forwards requests to configured LLM providers. It explains the endpoint format, request structure, and provides examples using cURL and the OpenAI Python SDK. ```APIDOC ## Proxy API Endpoint **Format:** `http://{host}:{port}/{model_id}/v1/{endpoint}` All OpenAI-compatible endpoints are transparently forwarded to the configured provider. The `Authorization` header is automatically replaced with the stored API key. If a model name is configured in LLM Gate, the `model` field in the request body is overridden with that value. A dummy key (any string) must be provided in the client; LLM Gate substitutes it automatically. ### Request Example (cURL - Chat Completions) ```bash curl http://localhost:11456/my-gpt4/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-dummy" \ -d '{ "model": "gpt-4", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello, introduce yourself."} ] }' ``` ### Response Example (cURL - Chat Completions) ``` # Response: standard OpenAI chat completion JSON # The real API key stored in LLM Gate is injected; "sk-dummy" is never forwarded. ``` ### Request Example (cURL - List Models) ```bash curl http://localhost:11456/my-gpt4/v1/models \ -H "Authorization: Bearer sk-dummy" ``` ### Response Example (cURL - List Models) ``` # Response: { "data": [{ "id": "gpt-4", ... }, ...] } ``` ### Request Example (OpenAI Python SDK) ```python from openai import OpenAI client = OpenAI( base_url="http://localhost:11456/my-gpt4/v1", api_key="sk-dummy" # Any string; LLM Gate replaces it with the real key ) response = client.chat.completions.create( model="gpt-4", # Overridden if 'Model Name' is set in LLM Gate messages=[{"role": "user", "content": "What is the capital of France?"}] ) print(response.choices[0].message.content) ``` ### Response Example (OpenAI Python SDK) ``` # Output: "The capital of France is Paris." ``` ``` -------------------------------- ### Build LLM Gate Application Source: https://github.com/xuanzhi33/llm-gate/blob/main/AGENTS.md Builds the frontend assets and then the full application bundle for distribution. ```bash pnpm build # Builds frontend pnpm tauri build # Builds full app bundle ``` -------------------------------- ### Build for Production Source: https://github.com/xuanzhi33/llm-gate/blob/main/README.md Execute this command to create a production build of the application. This command is used for deploying the application. ```bash pnpm tauri build ``` -------------------------------- ### LLM Gate Proxy Server State Management with Pinia Store Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Illustrates how to use the `useProxyStore` Pinia store to manage the LLM Gate server's lifecycle and access its reactive state. Ensure `initListeners` is called on app mount. ```typescript import { useProxyStore } from '@/stores/proxy' import { useSettingsStore } from '@/stores/settings' const proxyStore = useProxyStore() const settingsStore = useSettingsStore() // Initialize event listeners (call once on app mount) await proxyStore.initListeners() // Subscribes to 'proxy-status-change' → updates status/host/port/requestCount // Subscribes to 'proxy-request' → increments requestCount, logs forwarded requests // Start the server (reads host/port from settingsStore) await proxyStore.start() // If already running, automatically calls restart() // Check reactive state console.log(proxyStore.status) // 'running' | 'stopped' console.log(proxyStore.host) // '127.0.0.1' or '0.0.0.0' console.log(proxyStore.port) // e.g. 11456 console.log(proxyStore.requestCount) // number of proxied requests since last start // Stop the server await proxyStore.stop() // Restart (stop then start) await proxyStore.restart() // Toggle helper (used by ServiceStatusCard) async function toggleProxy() { if (proxyStore.status === 'running') { await proxyStore.stop() } else { await proxyStore.start() } } ``` -------------------------------- ### Application Bootstrap and Startup Sequence Source: https://context7.com/xuanzhi33/llm-gate/llms.txt The main entry point for the Vue application, setting up Pinia, Vue Router, and vue-i18n. The `onMounted` hook in `App.vue` details the sequence of operations for a full startup. ```typescript // src/main.ts import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import { i18n } from './i18n/config' createApp(App).use(createPinia()).use(router).use(i18n).mount('#app') // App.vue onMounted startup sequence: // 1. settingsStore.applyColorMode() — apply saved theme to + Tauri window // 2. settingsStore.applyLanguage() — sync vue-i18n locale, set document.title // 3. modelsStore.loadFromDisk() — read model-config.json from AppLocalData // 4. proxyStore.initListeners() — subscribe to Tauri IPC events // 5. errorStore.initListeners() — subscribe to proxy-error events // 6. updateStore.checkUpdate() — check GitHub Releases for new version // 7. initTray(t) — create system tray icon // 8. if (autoStart) proxyStore.start() — start gateway if setting is enabled // Vue Router routes (hash-based history): // / → HomeView (ServiceStatusCard + ModelConnectionCard) // /models → ModelsView (CRUD list of model configs + Quick Add Wizard) // /settings → SettingsView (theme, language, port, logging, update, about) ``` -------------------------------- ### Use OpenAI Python Library with LLM Gate Source: https://github.com/xuanzhi33/llm-gate/blob/main/README.md Demonstrates how to configure and use the OpenAI Python library to interact with LLM Gate. The `base_url` should point to your local LLM Gate instance, and any API key can be used as a placeholder. ```python from openai import OpenAI client = OpenAI( base_url="http://localhost:11456/my-gpt4/v1", api_key="dummy-key" # You can fill in anything here; LLM Gate will inject the real key ) response = client.chat.completions.create( model="gpt-4", # If you provided a "Model Name" in LLM Gate, this will be automatically replaced messages=[ {"role": "user", "content": "Hello, please introduce yourself."} ] ) print(response.choices[0].message.content) ``` -------------------------------- ### Using OpenAI-Python SDK with LLM Gate Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Shows how to configure the `openai-python` SDK to use the LLM Gate proxy. Set the `base_url` to the LLM Gate endpoint and use any dummy string for `api_key`. ```python # Using openai-python SDK # pip install openai from openai import OpenAI client = OpenAI( base_url="http://localhost:11456/my-gpt4/v1", api_key="sk-dummy" # Any string; LLM Gate replaces it with the real key ) response = client.chat.completions.create( model="gpt-4", # Overridden if 'Model Name' is set in LLM Gate messages=[{"role": "user", "content": "What is the capital of France?"}] ) print(response.choices[0].message.content) # Output: "The capital of France is Paris." ``` -------------------------------- ### Show and Focus Application Window Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Use `showWindow` to ensure the main application window is visible and focused. This utility handles cases where the window might be minimized or hidden. ```typescript import { showWindow } from '@/utils/window' // Unminimizes (if minimized), shows (if hidden), then focuses the window await showWindow() ``` -------------------------------- ### Manage Application Settings with `useSettingsStore` Source: https://context7.com/xuanzhi33/llm-gate/llms.txt This store manages user preferences like theme, language, and server settings, persisting them to `localStorage`. Changes to `serverPort` or `exposeServer` automatically restart the proxy if it's running. ```typescript import { useSettingsStore } from '@/stores/settings' const settings = useSettingsStore() // Read settings (all are reactive refs backed by localStorage) console.log(settings.serverPort) // number, default: 11456 console.log(settings.colorMode) // 'light' | 'dark' | 'system' console.log(settings.language) // 'en' | 'zh' | 'zh-hant' console.log(settings.autoStart) // boolean, default: true console.log(settings.exposeServer) // boolean, default: false console.log(settings.enableLogging) // boolean, default: false console.log(settings.isDarkMode) // computed: resolves 'system' using OS preference console.log(settings.host) // computed: '127.0.0.1' | '0.0.0.0' // Change port (proxy auto-restarts if currently running) settings.serverPort = 8080 // Expose to LAN (proxy auto-restarts; host becomes '0.0.0.0') settings.exposeServer = true // Switch language settings.language = 'zh' settings.applyLanguage() // Updates vue-i18n locale and document.title // Apply color mode to DOM and Tauri window settings.colorMode = 'dark' settings.applyColorMode() // Adds/removes 'dark' class on , calls setTheme() ``` -------------------------------- ### Initialize System Tray Icon Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Call `initTray` once on app mount. It sets up a system tray icon with localization support that automatically re-creates itself when the app language changes. ```typescript import { initTray } from '@/utils/tray' import { useI18n } from 'vue-i18n' const { t } = useI18n() // Call once on app mount; watches language changes internally await initTray(t) // Creates a tray icon (MAIN_TRAY) with: // - Left-click: shows/focuses the main window // - Right-click menu: // • "Show main window" → showWindow() // • "Quit" → exit() // - Tooltip: "LLM Gate" ``` -------------------------------- ### Manage Model Configurations with `useModelConfigStore` Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Use this store to perform CRUD operations on model configurations. Changes are debounced and auto-saved to `model-config.json`. Call `loadFromDisk()` on startup and `saveNow()` for immediate persistence. ```typescript import { useModelConfigStore } from '@/stores/models' import type { ModelConfig } from '@/stores/models' const modelStore = useModelConfigStore() // Load persisted configs on startup await modelStore.loadFromDisk() // Add a new model config modelStore.addConfig({ id: 'deepseek-chat', // Unique local identifier, used in the proxy URL baseUrl: 'https://api.deepseek.com', apiKey: 'sk-real-key-here', model: 'deepseek-chat', // Optional: overrides 'model' field in forwarded requests }) // Read all configs console.log(modelStore.configs) // [{ id: 'deepseek-chat',baseUrl: '...', apiKey: '...', model: '...' }] console.log(modelStore.count) // 1 // Look up by ID const cfg: ModelConfig | undefined = modelStore.getById('deepseek-chat') // Partial update (e.g., rotate API key) modelStore.updateConfig('deepseek-chat', { apiKey: 'sk-new-key' }) // Remove modelStore.removeConfig('deepseek-chat') // Reset all (clears in-memory list; does NOT delete the file) modelStore.reset() // Force immediate disk write (debounce bypass) await modelStore.saveNow() ``` -------------------------------- ### List Supported LLM Providers Source: https://context7.com/xuanzhi33/llm-gate/llms.txt The `providerList` array contains static configuration for supported LLM providers, including their API base URLs and key acquisition URLs. It's used in the Quick Add Wizard. ```typescript import { providerList } from '@/configs/providers' console.log(providerList) // [ // { name: 'OpenAI', url: 'https://api.openai.com/v1', keyUrl: 'https://platform.openai.com/api-keys' }, // { name: 'DeepSeek', url: 'https://api.deepseek.com', keyUrl: 'https://platform.deepseek.com/api_keys' }, // { name: 'SiliconFlow CN (...)', url: 'https://api.siliconflow.cn/v1', keyUrl: 'https://cloud.siliconflow.cn/account/ak' }, // { name: 'SiliconFlow Global', url: 'https://api.siliconflow.com/v1', keyUrl: 'https://cloud.siliconflow.com/account/ak' }, // { name: 'Google Gemini', url: 'https://generativelanguage.googleapis.com/v1beta/openai/', keyUrl: 'https://aistudio.google.com/app/apikey' }, // { name: 'OpenRouter', url: 'https://openrouter.ai/api/v1', keyUrl: 'https://openrouter.ai/keys' }, // ] // Find a provider and open its key portal const openai = providerList.find(p => p.name === 'OpenAI')! // Use openai.url as the Base URL for a new ModelConfig // Use openai.keyUrl to direct users to obtain a key ``` -------------------------------- ### useSettingsStore — Application Settings Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Persists user preferences like color theme, UI language, and proxy settings to `localStorage`. Automatically restarts the proxy when relevant settings change. ```APIDOC ## `useSettingsStore` — Application Settings Pinia store (`src/stores/settings.ts`) that persists user preferences to `localStorage` via `@vueuse/core`'s `useStorage`. Manages color theme, UI language, proxy port, auto-start, and LAN exposure. When `serverPort` or `exposeServer` changes while the proxy is running, it automatically calls `proxyStore.restart()`. ```typescript import { useSettingsStore } from '@/stores/settings' const settings = useSettingsStore() // Read settings (all are reactive refs backed by localStorage) console.log(settings.serverPort) // number, default: 11456 console.log(settings.colorMode) // 'light' | 'dark' | 'system' console.log(settings.language) // 'en' | 'zh' | 'zh-hant' console.log(settings.autoStart) // boolean, default: true console.log(settings.exposeServer) // boolean, default: false console.log(settings.enableLogging) // boolean, default: false console.log(settings.isDarkMode) // computed: resolves 'system' using OS preference console.log(settings.host) // computed: '127.0.0.1' | '0.0.0.0' // Change port (proxy auto-restarts if currently running) settings.serverPort = 8080 // Expose to LAN (proxy auto-restarts; host becomes '0.0.0.0') settings.exposeServer = true // Switch language settings.language = 'zh' settings.applyLanguage() // Updates vue-i18n locale and document.title // Apply color mode to DOM and Tauri window settings.colorMode = 'dark' settings.applyColorMode() // Adds/removes 'dark' class on , calls setTheme() ``` ``` -------------------------------- ### Handle Global Errors with `useErrorStore` Source: https://context7.com/xuanzhi33/llm-gate/llms.txt This store drives the application-level error dialog and listens for `proxy-error` Tauri events. Call `initListeners()` on app mount to enable event listening. Use `showError()` to programmatically display errors. ```typescript import { useErrorStore } from '@/stores/error' const errorStore = useErrorStore() // Initialize Tauri event listener (call once on app mount) // Listens to 'proxy-error' events: { title: string, message: string } await errorStore.initListeners() // Programmatically show the error dialog errorStore.showError( 'Failed to start the gateway server', 'Address already in use: port 11456' ) // Reactive state consumed by console.log(errorStore.isOpen) // true console.log(errorStore.title) // 'Failed to start the gateway server' console.log(errorStore.message) // 'Address already in use: port 11456' // Dismiss the dialog (clears title/message after 300ms transition) errorStore.close() ``` -------------------------------- ### Run LLM Gate Tests and Quality Checks Source: https://github.com/xuanzhi33/llm-gate/blob/main/AGENTS.md Executes unit tests, linting, code formatting, and TypeScript type checking to ensure code quality. ```bash pnpm test:unit # Vitest unit tests pnpm lint # ESLint with auto-fix pnpm format # Prettier formatting pnpm type-check # TypeScript type checking ``` -------------------------------- ### Proxy API Endpoint Usage with cURL Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Demonstrates how to interact with the LLM Gate proxy API using cURL for chat completions and listing models. Ensure a dummy API key is used, as LLM Gate will substitute it with the actual stored key. ```bash # Chat completions — model 'my-gpt4' configured to point at OpenAI curl http://localhost:11456/my-gpt4/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-dummy" \ -d '{ "model": "gpt-4", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello, introduce yourself."} ] }' # Response: standard OpenAI chat completion JSON # The real API key stored in LLM Gate is injected; "sk-dummy" is never forwarded. ``` ```bash # List available models through the proxy curl http://localhost:11456/my-gpt4/v1/models \ -H "Authorization: Bearer sk-dummy" # Response: { "data": [{ "id": "gpt-4", ... }, ...] } ``` -------------------------------- ### useErrorStore — Global Error Dialog Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Drives the application-level error dialog and listens for `proxy-error` Tauri events to surface backend errors. ```APIDOC ## `useErrorStore` — Global Error Dialog Pinia store (`src/stores/error.ts`) that drives the application-level error dialog. It also listens for `proxy-error` Tauri events emitted by the Rust backend when a fatal proxy error occurs, surfacing them in the UI without requiring component-level error handling. ```typescript import { useErrorStore } from '@/stores/error' const errorStore = useErrorStore() // Initialize Tauri event listener (call once on app mount) // Listens to 'proxy-error' events: { title: string, message: string } await errorStore.initListeners() // Programmatically show the error dialog errorStore.showError( 'Failed to start the gateway server', 'Address already in use: port 11456' ) // Reactive state consumed by console.log(errorStore.isOpen) // true console.log(errorStore.title) // 'Failed to start the gateway server' console.log(errorStore.message) // 'Address already in use: port 11456' // Dismiss the dialog (clears title/message after 300ms transition) errorStore.close() ``` ``` -------------------------------- ### useModelConfigStore — Model Configuration CRUD Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Manages a list of `ModelConfig` entries persisted to `model-config.json`. Provides CRUD operations and an immediate `saveNow` function. ```APIDOC ## `useModelConfigStore` — Model Configuration CRUD Pinia store (`src/stores/models.ts`) that manages the list of `ModelConfig` entries persisted to `model-config.json` in the app's local data directory. Changes are auto-saved to disk with a 1-second debounce. Provides full CRUD operations plus an immediate `saveNow` for cases where the backend must read updated data right away (e.g., the wizard flow). ```typescript import { useModelConfigStore } from '@/stores/models' import type { ModelConfig } from '@/stores/models' const modelStore = useModelConfigStore() // Load persisted configs on startup await modelStore.loadFromDisk() // Add a new model config modelStore.addConfig({ id: 'deepseek-chat', // Unique local identifier, used in the proxy URL baseUrl: 'https://api.deepseek.com', apiKey: 'sk-real-key-here', model: 'deepseek-chat', // Optional: overrides 'model' field in forwarded requests }) // Read all configs console.log(modelStore.configs) // [{ id: 'deepseek-chat', baseUrl: '...', apiKey: '...', model: '...' }] console.log(modelStore.count) // 1 // Look up by ID const cfg: ModelConfig | undefined = modelStore.getById('deepseek-chat') // Partial update (e.g., rotate API key) modelStore.updateConfig('deepseek-chat', { apiKey: 'sk-new-key' }) // Remove modelStore.removeConfig('deepseek-chat') // Reset all (clears in-memory list; does NOT delete the file) modelStore.reset() // Force immediate disk write (debounce bypass) await modelStore.saveNow() ``` ``` -------------------------------- ### getDomainName — URL to Display Name Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Extracts a short human-readable domain label from a full URL. Used in the UI to display provider names. ```APIDOC ## `getDomainName` — URL to Display Name Utility function that extracts a short human-readable domain label from a full URL. ### `getDomainName(url: string)` Extracts the domain name from a given URL. - **`url`** (string) - Required - The URL to parse. - **Returns** (string): The extracted domain name. ### Example Usage ```typescript import { getDomainName } from '@/utils/url' getDomainName('https://api.openai.com/v1') // 'openai' getDomainName('https://api.deepseek.com') // 'deepseek' getDomainName('https://generativelanguage.googleapis.com/v1beta/openai/') // 'googleapis' getDomainName('http://localhost:11456') // 'localhost' getDomainName('192.168.1.100') // '192.168.1.100' getDomainName('') // '' ``` ``` -------------------------------- ### Save and Load JSON Data Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Utility functions for persisting and retrieving JSON data within the Tauri app's local data directory. Automatically handles directory creation and returns null if a file does not exist. ```typescript import { saveJSON, loadJSON, saveText, loadText } from '@/utils/app-data' // Persist an array of objects to 'model-config.json' await saveJSON([ { id: 'openai', baseUrl: 'https://api.openai.com/v1', apiKey: 'sk-...', model: 'gpt-4o' } ], 'model-config.json') // Load back (returns null if file does not exist) const configs = await loadJSON<{ id: string; apiKey: string }[]>('model-config.json') if (configs) { console.log(configs[0].id) // 'openai' } // Raw text variants await saveText('hello', 'notes.txt') const text = await loadText('notes.txt') // 'hello' | null ``` -------------------------------- ### saveJSON / loadJSON — App Data Persistence Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Utility functions for reading and writing JSON files in the Tauri app's local data directory. Automatically creates the directory if it does not exist. ```APIDOC ## `saveJSON` / `loadJSON` — App Data Persistence Utility functions for reading and writing JSON files in the Tauri app's local data directory (`AppLocalData`). ### `saveJSON(data: any[], filename: string)` Persists an array of objects to a JSON file. - **`data`** (any[]) - Required - The data to save. - **`filename`** (string) - Required - The name of the file to save to. ### `loadJSON(filename: string)` Loads JSON data from a file. Returns `null` if the file does not exist. - **`filename`** (string) - Required - The name of the file to load. - **Returns** (T | null): The loaded data or null. ### `saveText(text: string, filename: string)` Saves raw text to a file. - **`text`** (string) - Required - The text content to save. - **`filename`** (string) - Required - The name of the file to save to. ### `loadText(filename: string)` Loads raw text from a file. Returns `null` if the file does not exist. - **`filename`** (string) - Required - The name of the file to load. - **Returns** (string | null): The loaded text or null. ### Example Usage ```typescript import { saveJSON, loadJSON, saveText, loadText } from '@/utils/app-data' // Persist data await saveJSON([{ id: 'openai', baseUrl: 'https://api.openai.com/v1', apiKey: 'sk-...', model: 'gpt-4o' }], 'model-config.json') // Load data const configs = await loadJSON<{ id: string; apiKey: string }[]>('model-config.json') if (configs) { console.log(configs[0].id) } // Text persistence await saveText('hello', 'notes.txt') const text = await loadText('notes.txt') ``` ``` -------------------------------- ### log — Conditional Logging Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Writes to the Tauri log plugin (and console.log) only when debug logging is enabled in settings. Supports info, error, and warn levels. ```APIDOC ## `log` — Conditional Logging Utility function that writes to the Tauri log plugin and `console.log` only when debug logging is enabled. ### `log(message: string, level?: 'info' | 'error' | 'warn')` Logs a message with an optional log level. - **`message`** (string) - Required - The message to log. - **`level`** ('info' | 'error' | 'warn') - Optional - The log level. Defaults to 'info'. ### Example Usage ```typescript import { log } from '@/utils/log' // Only emits output if settingsStore.enableLogging === true log('Gateway server is running at 127.0.0.1:11456') // level: 'info' (default) log('Failed to parse response', 'error') // level: 'error' log('Port may be in use', 'warn') // level: 'warn' // Output (when logging enabled): // [INFO] Gateway server is running at 127.0.0.1:11456 // Written to Tauri log file via @tauri-apps/plugin-log ``` ``` -------------------------------- ### Vue-I18n Translation and Locale Management Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Configure internationalization with `vue-i18n` supporting English, Simplified Chinese, and Traditional Chinese. The locale defaults to English but can be switched programmatically. ```typescript import { i18n, languageNames } from '@/i18n/config' import { useI18n } from 'vue-i18n' // In a Vue component const { t, locale } = useI18n() t('common.title') // 'LLM Gate' t('home.status.listening', { host: '127.0.0.1', port: 11456 }) // 'Listening on 127.0.0.1:11456' t('info.proxyStarted', { port: 11456 }) // 'Gateway server started on port 11456' t('common.modelCount', 3) // '3 models' // Available locales and display names console.log(languageNames) // { en: 'English', zh: '简体中文 - Chinese (Simplified)', 'zh-hant': '繁體中文 - Chinese (Traditional)' } // Switch locale programmatically locale.value = 'zh' ``` -------------------------------- ### Conditional Logging with `log` Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Writes messages to the Tauri log plugin and console only when debug logging is enabled in settings. Supports 'info', 'error', and 'warn' levels. ```typescript import { log } from '@/utils/log' // Only emits output if settingsStore.enableLogging === true log('Gateway server is running at 127.0.0.1:11456') // level: 'info' (default) log('Failed to parse response', 'error') // level: 'error' log('Port may be in use', 'warn') // level: 'warn' // Output (when logging enabled): // [INFO] Gateway server is running at 127.0.0.1:11456 // Written to Tauri log file via @tauri-apps/plugin-log ``` -------------------------------- ### Extract Domain Name from URL Source: https://context7.com/xuanzhi33/llm-gate/llms.txt Extracts a human-readable domain label from a URL, useful for displaying provider names in the UI. Handles various URL formats and edge cases like empty strings. ```typescript import { getDomainName } from '@/utils/url' getDomainName('https://api.openai.com/v1') // 'openai' getDomainName('https://api.deepseek.com') // 'deepseek' getDomainName('https://generativelanguage.googleapis.com/v1beta/openai/') // 'googleapis' getDomainName('http://localhost:11456') // 'localhost' getDomainName('192.168.1.100') // '192.168.1.100' getDomainName('') // '' ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.