Skip to content

features reasoning and models

Nik Anand edited this page Jun 15, 2026 · 2 revisions

Reasoning and models

Active contributors: Nik, Ran

Purpose

DroidProxy exposes a fixed catalog of models that Factory Droid can use as custom models. Each model is defined once in src/Sources/DroidProxyModelCatalog.swift, then written into Factory's ~/.factory/settings.json customModels array by the Apply button in src/Sources/SettingsView.swift. The catalog carries each model's supported reasoning levels, but the proxy never injects reasoning. Droid CLI's per-session model picker reads the native reasoning metadata DroidProxy registers and forwards the chosen level upstream unchanged.

Model catalog

The full catalog from src/Sources/DroidProxyModelCatalog.swift. Antigravity, Kimi, and Cursor models all use provider: "openai" (or generic-chat-completion-api for Cursor) with a /v1 base URL so Droid sends POST /v1/chat/completions.

Display name baseModel idSlug providerKey levels (default) max tokens baseURL
Fable 5 claude-fable-5 fable-5 claude low/medium/high/xhigh/max (xhigh) 128000 http://localhost:8317
Opus 4.8 claude-opus-4-8 opus-4-8 claude low/medium/high/xhigh/max (xhigh) 128000 http://localhost:8317
Sonnet 4.6 claude-sonnet-4-6 sonnet-4-6 claude low/medium/high/max (high) 64000 http://localhost:8317
GPT 5.4 gpt-5.4 gpt-5.4 codex low/medium/high/xhigh (high) 128000 http://localhost:8317/v1
GPT 5.5 gpt-5.5 gpt-5.5 codex low/medium/high/xhigh (high) 128000 http://localhost:8317/v1
Gemini 3.1 Pro (High) gemini-pro-agent antigravity-gemini-3.1-pro antigravity high (high) 65536 http://localhost:8317/v1
Gemini 3.1 Pro (Low) gemini-3.1-pro-low gemini-3.1-pro-low antigravity low (low) 65536 http://localhost:8317/v1
Gemini 3 Flash gemini-3-flash antigravity-gemini-3-flash antigravity high (high) 65536 http://localhost:8317/v1
Gemini 3.5 Flash gemini-3-flash-agent gemini-3.5-flash antigravity medium/high (high) 65536 http://localhost:8317/v1
Gemini 3.5 Flash (Low) gemini-3.5-flash-low gemini-3.5-flash-low antigravity low (low) 65536 http://localhost:8317/v1
Gemini 3.1 Flash Lite gemini-3.1-flash-lite gemini-3.1-flash-lite antigravity high (high) 65536 http://localhost:8317/v1
Claude Sonnet 4.6 (Thinking) ag-c46s-thinking ag-c46s-thinking antigravity high (high) 64000 http://localhost:8317/v1
Claude Opus 4.6 (Thinking) ag-c46o-thinking ag-c46o-thinking antigravity high (high) 64000 http://localhost:8317/v1
GPT-OSS 120B (Medium) gpt-oss-120b-medium gpt-oss-120b-medium antigravity medium (medium) 32768 http://localhost:8317/v1
Kimi K2.6 kimi-k2.6 kimi-k2.6 kimi high (high) 262144 http://localhost:8317/v1
Cursor Composer 2.5 (beta) cursor-composer-2.5 cursor-composer-2.5 cursor high (high) 128000 http://localhost:8317/v1
Cursor Small (beta) cursor-small cursor-small cursor high (high) 64000 http://localhost:8317/v1

The two Cursor models are appended only when the BETA_FLAG UserDefaults flag is on; they use provider: "generic-chat-completion-api". The Antigravity models are built by the antigravityModel(...) helper, which fixes provider: "openai", providerKey: "antigravity", and baseURL: "http://localhost:8317/v1".

Key abstractions

Type Role
DroidProxyModelKind Enum tagging each definition: claudeAdaptive, codex, kimi, antigravity, cursor.
DroidProxyThinkingLevel A reasoning level as value (low/medium/high/xhigh/max) plus displayName.
DroidProxyModelDefinition One model: baseModel, idSlug, displayName, maxOutputTokens, provider, providerKey, baseURL, kind, levels, defaultLevelValue.
simpleID Computed custom:droidproxy:<idSlug> — the stable Factory id for the model.
settingsEntry Computed [String: Any] matching Factory's custom-model schema. When levels is non-empty it embeds enableThinking: true, the full supportedReasoningEfforts array, defaultReasoningEffort, and a reasoningEffort (the only level when there is exactly one, otherwise the defaultLevelValue). Keeping all reasoning metadata explicit means Droid/Factory does not have to infer the supported efforts from built-in model defaults. Antigravity entries prefix the display name with Antigravity:.
DroidProxyModelCatalog.definitions The ordered list of all definitions (Cursor entries gated by BETA_FLAG).
settingsModels(providerIsEnabled:) Maps definitions to settingsEntry dicts, skipping any whose providerKey is disabled.
allSettingsIDs Set of every simpleID, used to detect and remove prior DroidProxy entries.

How it works

Apply flow (writing settings.json)

applyFactoryCustomModels() in src/Sources/SettingsView.swift is the writer.

graph TD
    A[User clicks Apply / Re-apply] --> B[Read ~/.factory/settings.json]
    B --> C[Take existing customModels array]
    C --> D[Remove entries whose id is in allSettingsIDs,\nlegacy ids, custom:droidproxy:*, or custom:CC:*]
    D --> E[enabledFactorySettingsModels:\nsettingsModels filtered by provider enabled state]
    E --> F[Append entries, reindex with index = startIndex + offset]
    F --> G[Write timestamped settings.json.droidproxy-*.bak backup]
    G --> H[Serialize prettyPrinted + sortedKeys]
    H --> I[Unescape backslash-slash to plain slash]
    I --> J[Atomic write to settings.json]
    J --> K[factoryModelsInstalled = true]
Loading

enabledFactorySettingsModels() calls DroidProxyModelCatalog.settingsModels with a closure that maps each providerKey to a ServiceType and checks serverManager.isProviderEnabled. checkFactoryModelsInstalled() compares the set of DroidProxy ids currently in settings.json against the expected enabled set to drive the "Applied" badge and the Apply/Re-apply label.

Request-time alias rewrite

For two Antigravity Claude models, the baseModel sent by Droid is not the backend's model id. ThinkingProxy rewrites it in place (preserving JSON key order) before forwarding:

graph LR
    A[Request model field] --> B{antigravityModelAliases?}
    B -- ag-c46s-thinking --> C[claude-sonnet-4-6]
    B -- ag-c46o-thinking --> D[claude-opus-4-6-thinking]
    B -- otherwise --> E[forwarded unchanged]
Loading

A similar map rewrites cursor-composer-2.5 to composer-2.5. See ThinkingProxy for the full rewrite pipeline.

Key principle

The proxy does not inject reasoning, reasoning_effort, thinking, or output_config. Each settingsEntry advertises enableThinking, the supported efforts, and a default reasoningEffort; Droid CLI's per-session selector picks the level from that native metadata and sends it in the request body, which the proxy forwards unchanged.

One narrow exception keeps a model usable at its top level: Sonnet 4.6 exposes max in Droid's selector, but Sonnet's adaptive thinking rejects output_config.effort:max upstream (HTTP 400). When a Sonnet 4.6 request arrives with output_config.effort == "max", ThinkingProxy converts it to classic extended thinking (thinking:{type:enabled,budget_tokens}) and pins max_tokens. Lower efforts pass through as adaptive untouched. See Thinking proxy for the transform.

Integration points

  • src/Sources/SettingsView.swift reads the catalog (settingsModels, allSettingsIDs) to write and verify ~/.factory/settings.json.
  • src/Sources/ServerManager.swift supplies isProviderEnabled, which gates whether a provider's models are written.
  • src/Sources/ThinkingProxy.swift performs the Antigravity and Cursor model-alias rewrites at request time.

Entry points for modification (adding a model)

  1. Add a DroidProxyModelDefinition to definitions in src/Sources/DroidProxyModelCatalog.swift (use the antigravityModel(...) helper for Antigravity-routed models).
  2. Set its levels and defaultLevelValue; they flow into settingsEntry as supportedReasoningEfforts, defaultReasoningEffort, and reasoningEffort.
  3. If the backend expects a different model id than baseModel, add an entry to antigravityModelAliases (or cursorModelAliases) in src/Sources/ThinkingProxy.swift.
  4. Users click Apply in Settings to write the new entry into ~/.factory/settings.json.

Key source files

File Role
src/Sources/DroidProxyModelCatalog.swift Catalog of model definitions, levels, and settingsEntry schema.
src/Sources/SettingsView.swift applyFactoryCustomModels, enabledFactorySettingsModels, checkFactoryModelsInstalled.
src/Sources/ThinkingProxy.swift Request-time Antigravity/Cursor model-alias rewriting.
src/Sources/AuthStatus.swift ServiceType, which maps providerKeys to provider enable state.

Related

Clone this wiki locally