How to Add a Custom OpenAI-Compatible Endpoint for AI Summaries in Meetily

Set a custom OpenAI-compatible endpoint by passing customOpenAIEndpoint to the builtin_ai_generate_summary Tauri command, which routes requests to {endpoint}/chat/completions via the LLM client in src-tauri/src/summary/llm_client.rs.

Meetily's summarization system is designed to work with any OpenAI-compatible API, including private proxies, Azure OpenAI deployments, or self-hosted models like vLLM. The backend does not hard-code provider URLs; instead, it accepts a dynamic endpoint parameter at runtime. This guide walks through the complete flow—from frontend invocation to request construction—so you can integrate your own AI infrastructure.

Understanding the Architecture

Meetily uses a three-layer architecture for summary generation:

  1. Frontend (React/TypeScript) – Gathers user input and settings, then invokes the Tauri command.
  2. Tauri command layer – Receives arguments, validates the provider type, and delegates to the LLM client.
  3. LLM client (llm_client.rs) – Constructs the HTTP request using the provided endpoint.

The critical integration point is the custom_openai_endpoint parameter, which the LLM client appends to /chat/completions when provider is "custom-openai".

Calling the Tauri Command from the Frontend

The frontend triggers summary generation via Tauri's invoke API. Pass the provider name "custom-openai" and your endpoint URL as arguments.

import { invoke } from '@tauri-apps/api/tauri';

/**
 * Generate a meeting summary through a private OpenAI-compatible service.
 */
async function summarizeWithCustomEndpoint(
  transcript: string,
  apiKey: string,
  customEndpoint: string,
  model = 'gpt-4'
): Promise<string> {
  const summary = await invoke<string>('builtin_ai_generate_summary', {
    provider: 'custom-openai',
    modelName: model,
    apiKey,
    systemPrompt: 'Summarize this meeting transcript concisely.',
    userPrompt: transcript,
    customOpenAIEndpoint: customEndpoint,
    maxTokens: 1024,
    temperature: 0.7,
    topP: 0.9,
  });

  return summary;
}

// Example usage
const result = await summarizeWithCustomEndpoint(
  meetingTranscript,
  'sk-your-api-key',
  'https://my-internal-openai.example.com'
);

Key parameters:

  • provider: 'custom-openai' – Required to trigger the LLMProvider::CustomOpenAI branch in Rust.
  • customOpenAIEndpoint – The base URL of your OpenAI-compatible service (no trailing /chat/completions needed).

How the Rust Backend Processes the Request

The builtin_ai_generate_summary command in src-tauri/src/summary/summary_engine/commands.rs receives the frontend arguments and forwards them to the core generator.

#[tauri::command]
pub async fn builtin_ai_generate_summary(
    provider: String,
    model_name: String,
    api_key: String,
    system_prompt: String,
    user_prompt: String,
    custom_openai_endpoint: Option<String>,
    // ...additional optional parameters
) -> Result<String, String> {
    let provider = LLMProvider::from_str(&provider)?;
    let client = reqwest::Client::new();

    summary::llm_client::generate_summary(
        &client,
        &provider,
        &model_name,
        &api_key,
        &system_prompt,
        &user_prompt,
        None,                       // ollama_endpoint
        custom_openai_endpoint.as_deref(),  // <-- passed here
        // ...remaining parameters
    )
    .await
}

The custom_openai_endpoint is passed as Option<&str> to the generator, allowing None for non-custom providers.

URL Construction in the LLM Client

In src-tauri/src/summary/llm_client.rs (lines 173-179), the client builds the complete request URL:

let url = format!(
    "{}/chat/completions",
    endpoint.trim_end_matches('/')
);

This means your endpoint should be the base URL only. For example:

Your Input Constructed URL
https://api.internal.com https://api.internal.com/chat/completions
https://api.internal.com/ https://api.internal.com/chat/completions
https://api.internal.com/v1 https://api.internal.com/v1/chat/completions

The trim_end_matches('/') ensures consistent URL construction regardless of whether you include a trailing slash.

Persisting the Endpoint in Settings

To avoid passing the endpoint on every call, Meetily stores user preferences in JSON within the app-data directory. The settings structure includes:

{
  "summary": {
    "aiProvider": "custom-openai",
    "customOpenAIEndpoint": "https://my-openai-proxy.example.com",
    "customOpenAIModel": "gpt-4",
    "customOpenAIApiKey": "sk-..."
  }
}

When the settings UI updates this configuration, subsequent summary requests automatically read customOpenAIEndpoint and include it in the builtin_ai_generate_summary invocation.

Validating Your Custom Endpoint

Before integrating, verify your endpoint implements the OpenAI chat completions API:

  • HTTP method: POST
  • Path: /chat/completions (appended by Meetily)
  • Headers: Authorization: Bearer {apiKey}, Content-Type: application/json
  • Request body: Standard OpenAI chat format with model, messages, max_tokens, temperature, top_p
  • Response: JSON with choices[0].message.content

Test with curl before configuring Meetily:

curl -X POST https://your-endpoint.com/chat/completions \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4",
    "messages": [{"role": "user", "content": "Hello"}],
    "max_tokens": 50
  }'

Summary

  • Meetily supports custom OpenAI-compatible endpoints through the custom-openai provider type.
  • Pass your endpoint URL as customOpenAIEndpoint to the builtin_ai_generate_summary Tauri command.
  • The LLM client in src-tauri/src/summary/llm_client.rs constructs the full URL by appending /chat/completions to your base endpoint.
  • Store persistent configuration in Meetily's settings JSON to avoid manual parameter passing.
  • No backend rebuild is required—the endpoint is resolved at runtime per request.

Frequently Asked Questions

What provider name should I use for a custom OpenAI-compatible endpoint?

Use custom-openai as the provider value. This maps to LLMProvider::CustomOpenAI in the Rust code, which activates the custom endpoint logic. Other valid providers include "openai", "ollama", and "anthropic", but only "custom-openai" respects the customOpenAIEndpoint parameter.

Does Meetily support Azure OpenAI with this method?

Yes. Azure OpenAI Service exposes an OpenAI-compatible REST API. Set customOpenAIEndpoint to your Azure endpoint (e.g., https://your-resource.openai.azure.com/openai/deployments/your-deployment-name) and include your Azure API key in the apiKey parameter. Ensure your deployment name matches the modelName you specify.

Can I use a local model like vLLM or llama.cpp-server?

Absolutely. Self-hosted OpenAI-compatible servers work identically to remote ones. Use http://localhost:8000 or your local server address as customOpenAIEndpoint. For servers without authentication, pass any non-empty string as apiKey—Meetily requires the parameter but your server may ignore it.

Where does Meetily store the custom endpoint configuration?

Meetily persists settings in a JSON file within the platform-specific app-data directory, managed through src-tauri/src/config.rs. The settings UI writes to this store, and frontend components read the customOpenAIEndpoint value when calling builtin_ai_generate_summary. You can also modify this file directly when Meetily is not running.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →