ThunderAI/claude-spec/04-api-integrations.md
2026-04-20 22:58:02 +02:00

7.3 KiB

API Integrations

Connection Types

The active AI provider is controlled by the connection_type preference. Possible values:

connection_type value Provider
chatgpt_web ChatGPT Web (no API key, opens browser window)
chatgpt_api OpenAI API (ChatGPT via API key)
ollama_api Ollama (self-hosted LLM)
openai_comp_api OpenAI-compatible API
google_gemini_api Google Gemini API
anthropic_api Claude (Anthropic) API

The global default is chatgpt_web. Each special prompt (add_tags, spamfilter, etc.) can independently override this via its own {prefix}_connection_type pref.

Provider Configuration

Each provider has its own settings block in integration_options_config (options/mzta-options-default.js):

ChatGPT Web

Controlled via js/mzta-chatgpt.js. Opens a browser window to chatgpt.com, injects the prompt via DOM automation, and reads back the response. Settings: chatgpt_web_model, chatgpt_web_tempchat, chatgpt_web_project, chatgpt_web_custom_gpt, chatgpt_web_load_wait_time.

Content script js/lib/diff.js is injected into ChatGPT pages for diff-view support.

OpenAI API (chatgpt_api)

  • Module: js/api/openai_responses.js
  • Worker: js/workers/model-worker-openai_responses.js
  • Settings keys: chatgpt_api_key, chatgpt_model, chatgpt_developer_messages, chatgpt_temperature, chatgpt_store

Ollama (ollama_api)

  • Module: js/api/ollama.js
  • Worker: js/workers/model-worker-ollama.js
  • Settings keys: ollama_host, ollama_model, ollama_num_ctx, ollama_temperature, ollama_think, ollama_format_json
  • Requires CORS to be configured on the Ollama server

OpenAI-Compatible (openai_comp_api)

  • Module: js/api/openai_comp.js
  • Worker: js/workers/model-worker-openai_comp.js
  • Settings keys: openai_comp_host, openai_comp_model, openai_comp_api_key, openai_comp_use_v1, openai_comp_chat_name, openai_comp_temperature
  • Pre-configured providers: js/api/openai_comp_configs.js (DeepSeek, Grok, Mistral, OpenRouter, Perplexity)

Google Gemini (google_gemini_api)

  • Module: js/api/google_gemini.js
  • Worker: js/workers/model-worker-google_gemini.js
  • Settings keys: google_gemini_api_key, google_gemini_model, google_gemini_system_instruction, google_gemini_thinking_budget, google_gemini_temperature

Anthropic / Claude (anthropic_api)

  • Module: js/api/anthropic.js
  • Worker: js/workers/model-worker-anthropic.js
  • Settings keys: anthropic_api_key, anthropic_model, anthropic_version, anthropic_max_tokens, anthropic_system_prompt, anthropic_temperature, anthropic_extended_thinking_budget
  • Extended thinking: when anthropic_extended_thinking_budget > 0, the request body adds thinking: { type: 'enabled', budget_tokens: N } and omits temperature (the Claude API forbids setting temperature with extended thinking). Thinking output arrives in the SSE stream as content_block_delta events with delta.type === 'thinking_delta' and is forwarded to the webchat UI as newThinkingToken messages, captured into a thinkingAccumulator in the worker and passed on tokensDone.

Thinking output in the webchat UI

Two provider categories emit reasoning/thinking content:

  • Ollama / OpenAI Compatible: thinking arrives inline in the normal token stream wrapped in <think>…</think> tags. MessagesArea.flushAccumulatingMessage() strips these blocks from the rendered text and renders them as a <details class="thinking-block"> prepended to the answer. If an unterminated <think> is detected mid-stream, the flush is deferred until the closing tag arrives.
  • Anthropic: thinking is captured in the worker and posted to the controller as newThinkingToken. MessagesArea accumulates it and renders the same <details> block on final flush.

The global hide_thinking pref (default true) controls the initial open/collapsed state of the thinking block: true → collapsed, false → open. The user can always toggle by clicking. Thinking content is never discarded. Other providers (Google Gemini, OpenAI Responses, ChatGPT Web) are not affected by this UI logic.

Configuration Validation

For special prompts (mzta_specialCommand), required fields are validated in initWorker() (js/mzta-special-commands.js) before the worker is created. If a required field is empty, an Error with isConfigError = true is thrown. Validation covers:

Provider Required fields
chatgpt_api chatgpt_api_key, chatgpt_model
google_gemini_api google_gemini_api_key, google_gemini_model
ollama_api ollama_host, ollama_model
openai_comp_api openai_comp_host, openai_comp_model
anthropic_api anthropic_api_key, anthropic_model, anthropic_version

Validation is skipped when use_specific_api = true (i.e., the prompt's own api_type overrides the global setting — credentials come from the prompt config, not global prefs).

The isConfigError flag on the thrown error tells callers in mzta-background.js to display the error in the panel without saving it to storage — so the user can fix settings and retry cleanly.

Feature-specific routing of isConfigError:

  • summarize / translate / spamfilter: the error is shown in their dedicated panel (summary / translation / spam panel) and not persisted to storage.
  • add_tags: it has no dedicated panel, so the error is routed to the generic error panel via showGenericError(errMsg, source) in mzta-background.js, which broadcasts a showGenericError message to all tabs. The content script js/mzta-compose-script.js renders it as #mzta-generic-error inside #mzta-container. The panel is dismissible and reusable by any future feature without its own UI.

For regular prompts (openChatGPT()), validation still happens inside the listener callback after the API webchat window is created (unchanged behavior).

Web Worker Pattern

For all API-based providers (everything except ChatGPT Web), the call goes through a Web Worker:

mzta-background.js
  → creates new Worker('js/workers/model-worker-<provider>.js')
  → postMessage({ prompt, settings })
  → worker makes HTTP fetch to provider API
  → worker postMessage({ result }) back
  → background handles result

This keeps API calls off the main thread and avoids blocking the Thunderbird UI.

Optional Permissions

API calls require host permissions. These are declared as optional_permissions in manifest.json and requested at runtime:

  • https://*.chatgpt.com/* and https://*.openai.com/* for ChatGPT
  • https://*.anthropic.com/* for Claude
  • https://*/* and http://*/* for Ollama and OpenAI-compatible endpoints

Adding a New Provider

  1. Create js/api/<provider>.js with the API call logic
  2. Create js/workers/model-worker-<provider>.js that imports and calls the API module
  3. Add a new connection_type value constant
  4. Add settings keys to integration_options_config in options/mzta-options-default.js
  5. Add UI controls to options/mzta-options.html and options/mzta-options.js
  6. Add the new connection_type case to the dispatch logic in mzta-background.js
  7. Add required host permissions to manifest.json optional_permissions
  8. Add i18n strings to _locales/en/messages.json