diff --git a/docs-site/src/content/docs/guides/integrations.md b/docs-site/src/content/docs/guides/integrations.md index b4488b926d8..d2bd4e9e21e 100644 --- a/docs-site/src/content/docs/guides/integrations.md +++ b/docs-site/src/content/docs/guides/integrations.md @@ -623,6 +623,30 @@ Chat Completions endpoint. Choose a row from Droid's `/model` picker. Disable removes the managed rows; Undo restores the exact saved file. Other settings and custom models remain yours. +Open **Integrations → Factory Droid** (`/#integrations/droid`) to set a reasoning +default for each connected model. Choose from the model's supported efforts, +review the changes, then confirm. **No default** clears that model's draft setting; +use **Save / review changes** and confirm to apply the removal. +Models without a declared effort list show that no default is available. + +The default applies only when Droid omits an effort from its request. An explicit +request effort takes precedence over this default; existing OpenCodex pins and +caps still apply. Droid may continue to display **Dynamic** even when OpenCodex +applies the configured default. Requests without the Droid default header are +unaffected; the header is a request preference, not proof of client identity. +For combos and routing policies, each concrete target checks the preference +against its own effort list, so an incompatible first target does not remove it +from a compatible fallback. + +A saved default that the routed model no longer supports is ignored for requests. +Clear it in the panel, then review and confirm the change to remove it from Droid. + +Refresh preserves defaults while the exact `provider/model` selector remains +connected. Renaming a provider, model, or combo alias replaces that managed row +and clears its default; choose a default for the renamed row again. Disable +removes the defaults with the managed model rows, and Undo restores the saved +rows and their defaults together. + Models whose IDs or display names contain `,` or `]` are skipped because the managed selector cannot address them safely; export and managed settings show the same rows. A nonempty catalog with no addressable models is refused. diff --git a/docs-site/src/content/docs/ko/guides/integrations.md b/docs-site/src/content/docs/ko/guides/integrations.md index e39df9ea9da..f321c73ac5c 100644 --- a/docs-site/src/content/docs/ko/guides/integrations.md +++ b/docs-site/src/content/docs/ko/guides/integrations.md @@ -258,4 +258,27 @@ GitHub Copilot 데스크톱 앱에서 opencodex를 OpenAI 호환 모델 프로 ## Factory Droid +**연동 → Factory Droid** (`/#integrations/droid`)에서 연결된 모델별 추론 기본값을 +설정할 수 있습니다. 모델이 지원하는 effort를 선택하고 변경 내용을 검토한 뒤 +확인하세요. **기본값 없음**은 해당 모델의 편집 중인 값을 지웁니다. 실제로 적용하려면 +**변경 사항 저장 / 검토**에서 확인해야 합니다. 지원 effort +목록이 없는 모델에는 사용 가능한 기본값이 없다고 표시됩니다. + +이 값은 Droid 요청에 effort가 없을 때만 적용됩니다. 요청이 effort를 명시하면 +그 값을 사용하며, 기존 OpenCodex pin과 cap 정책은 계속 적용됩니다. OpenCodex가 +기본값을 적용하더라도 Droid에는 **Dynamic**으로 표시될 수 있습니다. Droid 기본값 +헤더가 없는 요청에는 영향을 주지 않습니다. 이 헤더는 요청의 선호 값이며, +클라이언트 신원을 증명하지 않습니다. +콤보와 라우팅 정책에서는 각 실제 대상이 자체 effort 목록으로 이 선호 값을 +확인하므로, 첫 대상이 지원하지 않아도 지원하는 다음 대상으로 전달됩니다. + +저장된 기본값을 라우팅된 모델이 더 이상 지원하지 않으면 요청에는 적용하지 +않습니다. Droid 설정에서도 제거하려면 패널에서 지운 뒤 변경 내용을 검토하고 +확인하세요. + +새로고침은 정확히 같은 `provider/model` 선택자가 계속 연결된 동안 기본값을 +유지합니다. 프로바이더, 모델, 콤보 별칭의 이름을 바꾸면 관리 행이 교체되고 +기본값이 지워지므로 새 행에서 다시 선택하세요. 연동을 해제하면 관리되는 모델 +행과 기본값이 함께 제거되며, 되돌리기는 저장된 행과 기본값을 함께 복원합니다. + Factory Droid는 `~/.factory/settings.json`(Windows에서는 `%USERPROFILE%\.factory\settings.json`)을 사용합니다. `ocx integration client enable --client droid`로 명시적으로 활성화한 다음 `/model`에서 사용자 지정 모델을 선택하세요. 관리되는 항목에는 키가 없으며 루프백에서만 동작합니다. 비활성화하면 관리되는 항목이 제거되고, Undo는 저장된 원본 바이트를 복원합니다. 기존 `config.json`에 OpenCodex 항목이 있거나 `settings.local.json`이 `customModels`를 덮어쓰면 활성화 전에 충돌을 해결하세요. [Factory BYOK 문서](https://docs.factory.ai/model-independence/byok)를 참고하세요. diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 0d41a71b036..0065b24a615 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -1483,6 +1483,16 @@ export const de: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "Standard für Reasoning-Aufwand", + "integrations.droidReasoning.description": "Der Standard gilt nur, wenn eine Anfrage keinen Reasoning-Aufwand angibt. Deaktiviere Factory Droid, um diese gespeicherten Standards zu entfernen.", + "integrations.droidReasoning.noDefault": "Kein Standard", + "integrations.droidReasoning.noEfforts": "Keine Optionen für die Reasoning-Stärke verfügbar", + "integrations.droidReasoning.noModels": "Keine exportierten Modelle verfügbar.", + "integrations.droidReasoning.modelDefault": "Standard-Reasoning-Aufwand für {model}", + "integrations.droidReasoning.unsupportedTitle": "Einige gespeicherte Standards werden nicht mehr unterstützt. Entferne sie; speichere und prüfe anschließend die Änderungen, um sie anzuwenden.", + "integrations.droidReasoning.unsupportedEffort": "Nicht unterstützter Aufwand: {effort}", + "integrations.droidReasoning.clear": "Standard entfernen", + "integrations.droidReasoning.saveReview": "Änderungen speichern / prüfen", "integrations.aside.profilesTitle": "Aside-Profile", "integrations.aside.profilesHint": "Wähle, welche Profile die ausgewählten Modelle erhalten. Das aktive Aside-Profil bleibt unverändert.", "integrations.aside.all": "Alle Profile synchronisieren", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index b71168448c5..4a626601016 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -2049,6 +2049,16 @@ export const en = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "Default reasoning effort", + "integrations.droidReasoning.description": "A default is used only when a request omits its reasoning effort. Disable Factory Droid to remove these saved defaults.", + "integrations.droidReasoning.noDefault": "No default", + "integrations.droidReasoning.noEfforts": "No reasoning-effort options available", + "integrations.droidReasoning.noModels": "No exported models are available.", + "integrations.droidReasoning.modelDefault": "Default reasoning effort for {model}", + "integrations.droidReasoning.unsupportedTitle": "Some saved defaults are no longer supported. Clear them, then save / review changes to apply.", + "integrations.droidReasoning.unsupportedEffort": "Unsupported effort: {effort}", + "integrations.droidReasoning.clear": "Clear default", + "integrations.droidReasoning.saveReview": "Save / review changes", "integrations.aside.profilesTitle": "Aside profiles", "integrations.aside.profilesHint": "Choose which profiles receive the selected models. Aside’s active profile stays unchanged.", "integrations.aside.all": "Sync all profiles", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index 01b2f499219..4a07c3cb936 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -2021,6 +2021,16 @@ export const fr: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "Niveau de raisonnement par défaut", + "integrations.droidReasoning.description": "Le niveau par défaut s’applique uniquement si la requête ne précise aucun niveau de raisonnement. Désactivez Factory Droid pour supprimer ces valeurs enregistrées.", + "integrations.droidReasoning.noDefault": "Aucune valeur par défaut", + "integrations.droidReasoning.noEfforts": "Aucune option de niveau de raisonnement disponible", + "integrations.droidReasoning.noModels": "Aucun modèle exporté n’est disponible.", + "integrations.droidReasoning.modelDefault": "Niveau de raisonnement par défaut pour {model}", + "integrations.droidReasoning.unsupportedTitle": "Certaines valeurs enregistrées ne sont plus prises en charge. Supprimez-les, puis enregistrez / vérifiez les changements pour les appliquer.", + "integrations.droidReasoning.unsupportedEffort": "Niveau non pris en charge : {effort}", + "integrations.droidReasoning.clear": "Supprimer la valeur", + "integrations.droidReasoning.saveReview": "Enregistrer / vérifier les changements", "integrations.aside.profilesTitle": "Profils Aside", "integrations.aside.profilesHint": "Choisissez les profils qui recevront les modèles sélectionnés. Le profil actif dans Aside reste inchangé.", "integrations.aside.all": "Synchroniser tous les profils", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index e3f01974249..a0a636a0b2a 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -1916,6 +1916,16 @@ export const ja: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "推論レベルの既定値", + "integrations.droidReasoning.description": "リクエストに推論レベルが指定されていない場合にのみ既定値が使われます。Factory Droid を無効にすると、保存済みの既定値も削除されます。", + "integrations.droidReasoning.noDefault": "既定値なし", + "integrations.droidReasoning.noEfforts": "利用可能な推論強度の選択肢はありません", + "integrations.droidReasoning.noModels": "エクスポートされたモデルはありません。", + "integrations.droidReasoning.modelDefault": "{model} の推論レベルの既定値", + "integrations.droidReasoning.unsupportedTitle": "一部の保存済み既定値はサポートされなくなりました。削除した後、変更を保存 / 確認して適用してください。", + "integrations.droidReasoning.unsupportedEffort": "未サポートのレベル: {effort}", + "integrations.droidReasoning.clear": "既定値を削除", + "integrations.droidReasoning.saveReview": "変更を保存 / 確認", "integrations.aside.profilesTitle": "Asideのプロファイル", "integrations.aside.profilesHint": "選択したモデルを同期するプロファイルを選んでください。Asideで使用中のプロファイルは変わりません。", "integrations.aside.all": "すべてのプロファイルを同期", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 7c667aadc75..5e22d0f9a31 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -1527,6 +1527,16 @@ export const ko: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "기본 추론 강도", + "integrations.droidReasoning.description": "요청에 추론 강도가 지정되지 않은 경우에만 기본값을 사용합니다. Factory Droid를 비활성화하면 저장된 기본값도 제거됩니다.", + "integrations.droidReasoning.noDefault": "기본값 없음", + "integrations.droidReasoning.noEfforts": "사용 가능한 추론 강도 없음", + "integrations.droidReasoning.noModels": "내보낸 모델이 없습니다.", + "integrations.droidReasoning.modelDefault": "{model}의 기본 추론 강도", + "integrations.droidReasoning.unsupportedTitle": "저장된 기본값 중 일부가 더 이상 지원되지 않습니다. 지운 다음 변경 사항을 저장 / 검토해야 적용됩니다.", + "integrations.droidReasoning.unsupportedEffort": "지원하지 않는 강도: {effort}", + "integrations.droidReasoning.clear": "기본값 지우기", + "integrations.droidReasoning.saveReview": "변경 사항 저장 / 검토", "integrations.aside.profilesTitle": "Aside 프로필", "integrations.aside.profilesHint": "선택한 모델을 동기화할 프로필을 고르세요. Aside에서 사용 중인 프로필은 바뀌지 않습니다.", "integrations.aside.all": "모든 프로필 동기화", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 9f6b89fbf03..1945d41ed9f 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -2007,6 +2007,16 @@ export const ru: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "Уровень рассуждений по умолчанию", + "integrations.droidReasoning.description": "Значение по умолчанию применяется, только если запрос не задаёт уровень рассуждений. Отключите Factory Droid, чтобы удалить сохранённые значения.", + "integrations.droidReasoning.noDefault": "Без значения по умолчанию", + "integrations.droidReasoning.noEfforts": "Нет доступных вариантов уровня рассуждения", + "integrations.droidReasoning.noModels": "Нет доступных экспортированных моделей.", + "integrations.droidReasoning.modelDefault": "Уровень рассуждений по умолчанию для {model}", + "integrations.droidReasoning.unsupportedTitle": "Некоторые сохранённые значения больше не поддерживаются. Удалите их, затем сохраните / проверьте изменения, чтобы применить их.", + "integrations.droidReasoning.unsupportedEffort": "Неподдерживаемый уровень: {effort}", + "integrations.droidReasoning.clear": "Удалить значение", + "integrations.droidReasoning.saveReview": "Сохранить / проверить изменения", "integrations.aside.profilesTitle": "Профили Aside", "integrations.aside.profilesHint": "Выберите профили, в которые будут добавлены выбранные модели. Активный профиль Aside не изменится.", "integrations.aside.all": "Синхронизировать все профили", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index 596ddc56929..9081c67657e 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -2034,6 +2034,16 @@ export const tr: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "Varsayılan akıl yürütme düzeyi", + "integrations.droidReasoning.description": "Varsayılan yalnızca istek akıl yürütme düzeyi belirtmediğinde kullanılır. Kaydedilmiş varsayılanları kaldırmak için Factory Droid'u devre dışı bırakın.", + "integrations.droidReasoning.noDefault": "Varsayılan yok", + "integrations.droidReasoning.noEfforts": "Kullanılabilir akıl yürütme düzeyi seçeneği yok", + "integrations.droidReasoning.noModels": "Dışa aktarılmış model yok.", + "integrations.droidReasoning.modelDefault": "{model} için varsayılan akıl yürütme düzeyi", + "integrations.droidReasoning.unsupportedTitle": "Kaydedilmiş bazı varsayılanlar artık desteklenmiyor. Bunları temizleyin, ardından uygulamak için değişiklikleri kaydedip inceleyin.", + "integrations.droidReasoning.unsupportedEffort": "Desteklenmeyen düzey: {effort}", + "integrations.droidReasoning.clear": "Varsayılanı temizle", + "integrations.droidReasoning.saveReview": "Değişiklikleri kaydet / incele", "integrations.aside.profilesTitle": "Aside profilleri", "integrations.aside.profilesHint": "Seçili modellerin hangi profillere aktarılacağını seçin. Aside’ın etkin profili değişmez.", "integrations.aside.all": "Tüm profilleri eşitle", diff --git a/gui/src/i18n/vi.ts b/gui/src/i18n/vi.ts index fda93dd64fb..a07d696d913 100644 --- a/gui/src/i18n/vi.ts +++ b/gui/src/i18n/vi.ts @@ -2004,6 +2004,16 @@ export const vi: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "Mức suy luận mặc định", + "integrations.droidReasoning.description": "Chỉ dùng mức mặc định khi yêu cầu không chỉ định mức suy luận. Tắt Factory Droid để xóa các mức mặc định đã lưu.", + "integrations.droidReasoning.noDefault": "Không có mặc định", + "integrations.droidReasoning.noEfforts": "Không có tùy chọn mức độ suy luận khả dụng", + "integrations.droidReasoning.noModels": "Không có mô hình nào được xuất.", + "integrations.droidReasoning.modelDefault": "Mức suy luận mặc định cho {model}", + "integrations.droidReasoning.unsupportedTitle": "Một số mức mặc định đã lưu không còn được hỗ trợ. Hãy xóa chúng, sau đó lưu / xem lại thay đổi để áp dụng.", + "integrations.droidReasoning.unsupportedEffort": "Mức không được hỗ trợ: {effort}", + "integrations.droidReasoning.clear": "Xóa mặc định", + "integrations.droidReasoning.saveReview": "Lưu / xem lại thay đổi", "integrations.aside.profilesTitle": "Cấu hình Aside", "integrations.aside.profilesHint": "Chọn profile nào sẽ nhận các model được chọn. Profile đang hoạt động của Aside sẽ được giữ nguyên.", "integrations.aside.all": "Đồng bộ tất cả profile", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 46d84a91689..9e827acff60 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -2896,6 +2896,16 @@ export const zhTW: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "預設推理強度", + "integrations.droidReasoning.description": "只有在要求未指定推理強度時才會使用預設值。停用 Factory Droid 會移除這些已儲存的預設值。", + "integrations.droidReasoning.noDefault": "無預設值", + "integrations.droidReasoning.noEfforts": "沒有可用的推理強度選項", + "integrations.droidReasoning.noModels": "沒有可匯出的模型。", + "integrations.droidReasoning.modelDefault": "{model} 的預設推理強度", + "integrations.droidReasoning.unsupportedTitle": "部分已儲存的預設值已不再支援。清除後,請儲存 / 檢查變更以套用。", + "integrations.droidReasoning.unsupportedEffort": "不支援的強度:{effort}", + "integrations.droidReasoning.clear": "清除預設值", + "integrations.droidReasoning.saveReview": "儲存 / 檢查變更", "integrations.aside.profilesTitle": "Aside 設定檔", "integrations.aside.profilesHint": "選擇要接收所選模型的設定檔。Aside 目前使用的設定檔不會改變。", "integrations.aside.all": "同步所有設定檔", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 0ffc79db81f..902d4438e23 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -1500,6 +1500,16 @@ export const zh: Record = { "integrations.tab.cline": "Cline CLI", "integrations.tab.kilo": "Kilo", "integrations.tab.droid": "Factory Droid", + "integrations.droidReasoning.title": "默认推理强度", + "integrations.droidReasoning.description": "仅当请求未指定推理强度时才使用默认值。停用 Factory Droid 会删除这些已保存的默认值。", + "integrations.droidReasoning.noDefault": "无默认值", + "integrations.droidReasoning.noEfforts": "没有可用的推理强度选项", + "integrations.droidReasoning.noModels": "没有可导出的模型。", + "integrations.droidReasoning.modelDefault": "{model} 的默认推理强度", + "integrations.droidReasoning.unsupportedTitle": "部分已保存的默认值已不再受支持。清除后,请保存 / 检查更改以应用。", + "integrations.droidReasoning.unsupportedEffort": "不支持的强度:{effort}", + "integrations.droidReasoning.clear": "清除默认值", + "integrations.droidReasoning.saveReview": "保存 / 检查更改", "integrations.aside.profilesTitle": "Aside 配置文件", "integrations.aside.profilesHint": "选择要接收所选模型的配置文件。Aside 当前使用的配置文件不会改变。", "integrations.aside.all": "同步所有配置文件", diff --git a/gui/src/pages/integrations/DroidReasoningDefaultsPanel.tsx b/gui/src/pages/integrations/DroidReasoningDefaultsPanel.tsx new file mode 100644 index 00000000000..3238ed2b1ef --- /dev/null +++ b/gui/src/pages/integrations/DroidReasoningDefaultsPanel.tsx @@ -0,0 +1,99 @@ +import { useT } from "../../i18n/shared"; +import { Select } from "../../ui"; +import type { DroidReasoningStatus } from "./integration-api"; + +export default function DroidReasoningDefaultsPanel({ + reasoning, + defaults, + disabled, + onChange, + onReview, +}: { + reasoning: DroidReasoningStatus; + defaults: Record; + disabled: boolean; + onChange: (next: Record) => void; + onReview: () => void; +}) { + const t = useT(); + const effortsByModel = new Map(reasoning.models.map(model => [model.model, new Set(model.efforts)])); + const unsupported = Object.entries(defaults).filter(([modelId, effort]) => { + return !effortsByModel.get(modelId)?.has(effort); + }); + + const setDefault = (model: string, effort: string) => { + const next = { ...defaults }; + if (effort) next[model] = effort; + else delete next[model]; + onChange(next); + }; + + return ( +
+
+
+

{t("integrations.droidReasoning.title")}

+

{t("integrations.droidReasoning.description")}

+
+ +
+ + {reasoning.models.length > 0 ? ( +
    + {reasoning.models.map(model => { + const selected = defaults[model.model] ?? ""; + const declared = model.efforts.includes(selected); + const options = [ + { value: "", label: t("integrations.droidReasoning.noDefault") }, + ...(!declared && selected ? [{ + value: selected, + label: t("integrations.droidReasoning.unsupportedEffort", { effort: selected }), + }] : []), + ...model.efforts.map(effort => ({ value: effort, label: effort })), + ]; + return ( +
  • +
    + {model.label} + {model.model} +
    + {model.efforts.length > 0 ? ( +