ompchamber 2.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -11
- package/bunfig.toml +11 -0
- package/dist/client/index.html +16 -5
- package/dist/client/index.js +1393 -0
- package/dist/client/static/js/index-8t70jhyj.js +5149 -0
- package/dist/client/static/js/index-n4wkqj7t.css +1 -0
- package/index.html +54 -0
- package/package.json +9 -10
- package/public/apple-touch-icon.png +0 -0
- package/public/icon-192x192.png +0 -0
- package/public/icon.svg +60 -0
- package/public/manifest.webmanifest +38 -0
- package/src/cli/lib/commands/serve.js +9 -5
- package/src/cli/lib/runtime.js +18 -7
- package/src/client/components/common/code-editor/handler.test.ts +274 -0
- package/src/client/components/common/code-editor/handler.ts +175 -0
- package/src/client/components/common/code-editor/index.tsx +185 -220
- package/src/client/components/common/code-surface/index.tsx +109 -12
- package/src/client/components/common/lazy-panels.tsx +5 -1
- package/src/client/components/common/provider-icon/glyphs.ts +118 -0
- package/src/client/components/common/provider-icon/index.tsx +85 -0
- package/src/client/components/common/viewport.ts +5 -3
- package/src/client/components/layout/RightActivityBar.tsx +2 -1
- package/src/client/components/layout/desktop-layout/TopNavbar.tsx +21 -4
- package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +4 -0
- package/src/client/components/layout/desktop-layout/index.tsx +26 -4
- package/src/client/components/mobile/LayoutWrapper.tsx +16 -0
- package/src/client/components/mobile/RightSidebar.tsx +11 -4
- package/src/client/components/mobile/SessionSidebar.tsx +10 -0
- package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +0 -1
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +16 -1
- package/src/client/components/mobile/mobile-session-sidebar/List.tsx +3 -0
- package/src/client/components/settings/categories/ProviderSettings.tsx +45 -4
- package/src/client/components/settings/categories/UsageSettings.tsx +22 -7
- package/src/client/components/settings/categories/provider-settings/AddModelModal.tsx +322 -0
- package/src/client/components/settings/categories/provider-settings/AddProviderModal.tsx +152 -56
- package/src/client/components/settings/categories/provider-settings/AuthSection.tsx +52 -1
- package/src/client/components/settings/categories/provider-settings/DeleteProviderModal.tsx +133 -0
- package/src/client/components/settings/categories/provider-settings/DialectFields.tsx +136 -0
- package/src/client/components/settings/categories/provider-settings/EffortLadder.tsx +89 -0
- package/src/client/components/settings/categories/provider-settings/Header.tsx +2 -2
- package/src/client/components/settings/categories/provider-settings/LoginModal.tsx +2 -2
- package/src/client/components/settings/categories/provider-settings/ModelsList.tsx +22 -2
- package/src/client/components/settings/categories/provider-settings/PresetPicker.tsx +82 -0
- package/src/client/components/settings/categories/provider-settings/PriceFields.tsx +107 -0
- package/src/client/components/settings/categories/provider-settings/ReconnectModal.tsx +43 -22
- package/src/client/components/settings/categories/provider-settings/SidebarList.tsx +2 -2
- package/src/client/components/settings/categories/usage-settings/LimitsSection.tsx +115 -0
- package/src/client/components/settings/categories/usage-settings/ProviderCard.tsx +19 -26
- package/src/client/components/settings/categories/usage-settings/ProviderDetail.tsx +29 -22
- package/src/client/components/settings/categories/usage-settings/SidebarList.tsx +60 -48
- package/src/client/components/settings/categories/usage-settings/format.ts +36 -0
- package/src/client/components/settings/categories/usage-settings/providers.ts +52 -28
- package/src/client/components/workspace/browser-panel/AddressBar.tsx +1 -10
- package/src/client/components/workspace/browser-panel/Screencast.tsx +4 -5
- package/src/client/components/workspace/browser-panel/index.tsx +6 -6
- package/src/client/components/workspace/browser-toolbar/index.tsx +22 -12
- package/src/client/components/workspace/chat-timeline/AiMessageFooter.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/GeneratingIndicator.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Todo.tsx +1 -1
- package/src/client/components/workspace/diff-panel/Toolbar.tsx +71 -38
- package/src/client/components/workspace/diff-panel/index.tsx +139 -53
- package/src/client/components/workspace/editor/CommandPalette.test.ts +242 -0
- package/src/client/components/workspace/editor/CommandPalette.tsx +138 -0
- package/src/client/components/workspace/editor/FindWidget.test.ts +261 -0
- package/src/client/components/workspace/editor/FindWidget.tsx +219 -0
- package/src/client/components/workspace/editor/Tabs.tsx +2 -2
- package/src/client/components/workspace/editor/Toolbar.tsx +129 -26
- package/src/client/components/workspace/editor/index.tsx +180 -62
- package/src/client/components/workspace/file-explorer/Modals.tsx +79 -2
- package/src/client/components/workspace/file-explorer/TreeItem.tsx +43 -113
- package/src/client/components/workspace/model-dropdown/Item.tsx +13 -23
- package/src/client/components/workspace/model-dropdown/Panel.tsx +1 -1
- package/src/client/components/workspace/model-dropdown/Section.tsx +13 -4
- package/src/client/components/workspace/model-dropdown/index.tsx +7 -3
- package/src/client/components/workspace/todo-panel/PhaseGroup.tsx +76 -0
- package/src/client/components/workspace/todo-panel/Row.tsx +69 -0
- package/src/client/components/workspace/todo-panel/index.tsx +161 -0
- package/src/client/components/workspace/usage-panel/ProviderSelect.tsx +10 -5
- package/src/client/components/workspace/usage-panel/index.tsx +36 -18
- package/src/client/data/samples/dialogue-session.ts +1 -1
- package/src/client/data/samples/sample-tools/other.ts +1 -1
- package/src/client/data/settings/provider.ts +141 -9
- package/src/client/hooks/editor/find-harness.test-util.ts +170 -0
- package/src/client/hooks/editor/scroll-container.test.ts +102 -0
- package/src/client/hooks/editor/scroll-container.ts +26 -0
- package/src/client/hooks/editor/use-code-window.ts +2 -20
- package/src/client/hooks/editor/use-editor-find-write.test.ts +170 -0
- package/src/client/hooks/editor/use-editor-find.test.ts +118 -0
- package/src/client/hooks/editor/use-editor-find.ts +321 -0
- package/src/client/hooks/editor/use-editor-history.test.ts +216 -0
- package/src/client/hooks/editor/use-editor-history.ts +134 -0
- package/src/client/hooks/editor/use-file-editor.ts +24 -27
- package/src/client/hooks/editor/use-occurrences.test.ts +236 -0
- package/src/client/hooks/editor/use-occurrences.ts +98 -0
- package/src/client/hooks/models/use-provider-names.ts +1 -1
- package/src/client/hooks/settings/providers/actions.ts +337 -0
- package/src/client/hooks/settings/providers/api.ts +69 -0
- package/src/client/hooks/settings/providers/index.ts +138 -0
- package/src/client/hooks/settings/providers/messages.ts +77 -0
- package/src/client/hooks/settings/useUsageReport.ts +8 -3
- package/src/client/hooks/ui/theme.ts +3 -3
- package/src/client/hooks/workspace/file-tabs.ts +62 -11
- package/src/client/hooks/workspace/file-tree-actions.test.ts +348 -0
- package/src/client/hooks/workspace/file-tree-actions.ts +250 -0
- package/src/client/hooks/workspace/session-todos.ts +102 -0
- package/src/client/stubs/elkjs.ts +6 -1
- package/src/client/tailwind.css +30 -0
- package/src/server/index.ts +20 -2
- package/src/server/lib/assets/font-css.server.ts +86 -0
- package/src/server/lib/assets/fonts.server.ts +117 -0
- package/src/server/lib/bundler/css.ts +116 -0
- package/src/server/lib/lifecycle/shell-route.ts +17 -0
- package/src/server/lib/models/provider-listing.server.ts +257 -0
- package/src/server/lib/models/provider-registry.server.test.ts +122 -0
- package/src/server/lib/models/provider-registry.server.ts +76 -25
- package/src/server/lib/omp/config/models-config.ts +11 -0
- package/src/server/lib/omp/config/models-validation.test.ts +137 -0
- package/src/server/lib/omp/config/models-validation.ts +111 -2
- package/src/server/lib/omp/config/provider-seeds.test.ts +167 -0
- package/src/server/lib/omp/config/provider-seeds.ts +176 -0
- package/src/server/lib/omp/config/providers.ts +94 -100
- package/src/server/lib/omp/core/auth-credentials.ts +45 -0
- package/src/server/lib/omp/session/todos.ts +100 -0
- package/src/server/lib/omp/session/{usage.ts → usage/aggregate.ts} +1 -118
- package/src/server/lib/omp/session/usage/shaping.ts +130 -0
- package/src/server/lib/usage/deepseek.ts +4 -5
- package/src/server/lib/usage/kenari.ts +7 -6
- package/src/server/lib/usage/omp-usage.server.test.ts +126 -0
- package/src/server/lib/usage/omp-usage.server.ts +194 -0
- package/src/server/lib/usage/provider-key.ts +81 -3
- package/src/server/lib/usage/providers.server.test.ts +47 -0
- package/src/server/lib/usage/providers.server.ts +208 -0
- package/src/server/lib/usage/vendor-balance.server.test.ts +104 -0
- package/src/server/lib/usage/vendor-balance.server.ts +284 -0
- package/src/server/plugins/shell.server.ts +56 -0
- package/src/server/plugins/ssr.ts +24 -36
- package/src/server/plugins/static.test.ts +26 -29
- package/src/server/plugins/static.ts +40 -19
- package/src/server/routes/fs/action-create.test.ts +90 -0
- package/src/server/routes/fs/action.ts +23 -0
- package/src/server/routes/omp/index.ts +2 -0
- package/src/server/routes/omp/session-todos.ts +80 -0
- package/src/server/routes/settings/index.ts +2 -0
- package/src/server/routes/settings/provider-model.ts +107 -0
- package/src/server/routes/settings/provider-models.ts +65 -202
- package/src/server/routes/settings/providers.ts +22 -7
- package/src/server/routes/settings/usage.ts +125 -45
- package/src/server/routes/telemetry/tokens.ts +2 -1
- package/src/shared/lib/chat/{todo-parser.ts → todo/parser.ts} +17 -0
- package/src/shared/lib/chat/todo/progress.ts +97 -0
- package/src/shared/lib/chat/todo/snapshot.test.ts +156 -0
- package/src/shared/lib/chat/todo/snapshot.ts +215 -0
- package/src/shared/lib/code/editor/commands.test.ts +155 -0
- package/src/shared/lib/code/editor/commands.ts +260 -0
- package/src/shared/lib/code/editor/edits.test.ts +100 -0
- package/src/shared/lib/code/editor/edits.ts +122 -0
- package/src/shared/lib/code/editor/find-status.test.ts +50 -0
- package/src/shared/lib/code/editor/find-status.ts +36 -0
- package/src/shared/lib/code/editor/find.test.ts +203 -0
- package/src/shared/lib/code/editor/find.ts +224 -0
- package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.test.ts +1 -1
- package/src/shared/lib/code/editor/keymap.test.ts +109 -0
- package/src/shared/lib/code/editor/keymap.ts +151 -0
- package/src/shared/lib/code/editor/layers.test.ts +77 -0
- package/src/shared/lib/code/editor/layers.ts +67 -0
- package/src/shared/lib/code/editor/line-edits.test.ts +131 -0
- package/src/shared/lib/code/editor/line-edits.ts +159 -0
- package/src/shared/lib/code/editor/lines.test.ts +81 -0
- package/src/shared/lib/code/editor/lines.ts +83 -0
- package/src/shared/lib/code/editor/marks.test.ts +86 -0
- package/src/shared/lib/code/editor/marks.ts +106 -0
- package/src/shared/lib/code/editor/occurrences.test.ts +117 -0
- package/src/shared/lib/code/editor/occurrences.ts +111 -0
- package/src/shared/lib/code/editor/palette.test.ts +91 -0
- package/src/shared/lib/code/editor/palette.ts +92 -0
- package/src/shared/lib/code/editor/reset-css.test.ts +35 -0
- package/src/shared/lib/code/editor/reset-css.ts +32 -0
- package/src/shared/lib/code/editor/text-commands.test.ts +178 -0
- package/src/shared/lib/code/editor/text-commands.ts +154 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +75 -0
- package/src/shared/lib/code/syntax-highlight.ts +29 -17
- package/src/shared/lib/code/windowed-highlight.ts +52 -1
- package/src/shared/lib/fs/diff-parser.test.ts +116 -0
- package/src/shared/lib/fs/diff-parser.ts +86 -33
- package/src/shared/lib/models/{provider-connection.ts → provider/connection.ts} +37 -0
- package/src/shared/lib/models/provider/dialect.ts +179 -0
- package/src/shared/lib/models/provider/glyph.test.ts +116 -0
- package/src/shared/lib/models/provider/glyph.ts +200 -0
- package/src/shared/lib/models/{provider-models.ts → provider/models.ts} +91 -6
- package/src/shared/lib/models/provider/presets.test.ts +172 -0
- package/src/shared/lib/models/provider/presets.ts +83 -0
- package/src/shared/lib/ui/key-binding.test.ts +115 -0
- package/src/shared/lib/ui/key-binding.ts +143 -0
- package/src/shared/lib/util/platform.test.ts +87 -0
- package/src/shared/lib/util/platform.ts +30 -0
- package/src/shared/lib/workspace/file-tab-id.ts +29 -0
- package/src/shared/lib/workspace/keymap.test.ts +82 -0
- package/src/shared/lib/workspace/keymap.ts +30 -0
- package/src/shared/lib/workspace/refresh-cadence.ts +9 -0
- package/src/shared/lib/workspace/right-panels.ts +8 -0
- package/src/shared/lib/workspace/session-state/store.ts +0 -1
- package/src/shared/types/index.ts +1 -0
- package/src/shared/types/settings/provider.ts +81 -0
- package/src/shared/types/settings/usage.ts +88 -14
- package/src/shared/types/todo.ts +108 -0
- package/tsconfig.json +3 -1
- package/dist/client/static/css/index.5f630421b7.css +0 -2
- package/dist/client/static/font/KaTeX_AMS-Regular.a79f1c3119.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Caligraphic-Bold.ec17d13264.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Caligraphic-Regular.55fac25845.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Fraktur-Bold.d42a5579b0.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Fraktur-Regular.d3c882a649.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Bold.c3fb5ac22f.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-BoldItalic.6f2bb1dff2.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Italic.8916142bec.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Regular.0462f03bdf.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Math-BoldItalic.572d331f69.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Math-Italic.f28c23acad.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Bold.8c5b5494b6.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Italic.3b1e59b3ba.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Regular.ba21ed5f84.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Script-Regular.03e9641d6f.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size1-Regular.eae34984b3.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size2-Regular.5916a24fa3.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size4-Regular.10d95fd3a2.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Typewriter-Regular.a8709e3622.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-400-normal.2655b10c55.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-500-normal.92a991f204.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-600-normal.43788eb7f1.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-700-normal.2667215d33.woff2 +0 -0
- package/dist/client/static/js/77.0d81c74cf0.js +0 -139
- package/dist/client/static/js/77.0d81c74cf0.js.LICENSE.txt +0 -8
- package/dist/client/static/js/async/0.8e59f2eb5b.js +0 -1
- package/dist/client/static/js/async/0k.5852e6b4fa.js +0 -1
- package/dist/client/static/js/async/0w.9ed36d5860.js +0 -1
- package/dist/client/static/js/async/1.f387c12b63.js +0 -1
- package/dist/client/static/js/async/12.d091f91c99.js +0 -1
- package/dist/client/static/js/async/13.8f810a8a6c.js +0 -1
- package/dist/client/static/js/async/19.6fe909d55a.js +0 -160
- package/dist/client/static/js/async/1b.23f1141a38.js +0 -1
- package/dist/client/static/js/async/1k.973e8e21fc.js +0 -2
- package/dist/client/static/js/async/1k.973e8e21fc.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/1p.0d5cddda96.js +0 -1
- package/dist/client/static/js/async/2.b589f1733b.js +0 -1
- package/dist/client/static/js/async/2v.96165101de.js +0 -1
- package/dist/client/static/js/async/3.0cacc08423.js +0 -1
- package/dist/client/static/js/async/37.b28b2f61cd.js +0 -1
- package/dist/client/static/js/async/3d.5085fb913f.js +0 -1
- package/dist/client/static/js/async/3j.357c80ff38.js +0 -2
- package/dist/client/static/js/async/3j.357c80ff38.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/3p.bcf32a0983.js +0 -1
- package/dist/client/static/js/async/3r.3a69e98ee0.js +0 -64
- package/dist/client/static/js/async/3u.1228460e2a.js +0 -1
- package/dist/client/static/js/async/3y.3530b824ad.js +0 -198
- package/dist/client/static/js/async/3y.3530b824ad.js.LICENSE.txt +0 -5
- package/dist/client/static/js/async/4.9442eeb472.js +0 -1
- package/dist/client/static/js/async/4e.678285ca55.js +0 -1
- package/dist/client/static/js/async/4k.33a0c7eeb4.js +0 -1
- package/dist/client/static/js/async/4m.928a1dde85.js +0 -31
- package/dist/client/static/js/async/4m.928a1dde85.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/4q.aad20dab25.js +0 -1
- package/dist/client/static/js/async/5.ebaf8b89cc.js +0 -36
- package/dist/client/static/js/async/51.95bcae98af.js +0 -2
- package/dist/client/static/js/async/51.95bcae98af.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
- package/dist/client/static/js/async/5j.d6407c4248.js +0 -287
- package/dist/client/static/js/async/5o.88316d4460.js +0 -62
- package/dist/client/static/js/async/6.f034c9b528.js +0 -1
- package/dist/client/static/js/async/66.5dfa29f957.js +0 -93
- package/dist/client/static/js/async/6o.5b1a06a382.js +0 -1
- package/dist/client/static/js/async/7.e4a9288dd0.js +0 -1
- package/dist/client/static/js/async/73.308dff4da8.js +0 -1
- package/dist/client/static/js/async/7e.a1cc9bfd88.js +0 -1
- package/dist/client/static/js/async/7g.a7814350c3.js +0 -1
- package/dist/client/static/js/async/7l.fadbbc0583.js +0 -1
- package/dist/client/static/js/async/8.5067439d8c.js +0 -1
- package/dist/client/static/js/async/8j.ea110663fb.js +0 -1
- package/dist/client/static/js/async/a.bce4542392.js +0 -1
- package/dist/client/static/js/async/aa.65d3852576.js +0 -1
- package/dist/client/static/js/async/ar.9303e5f2e8.js +0 -2
- package/dist/client/static/js/async/ar.9303e5f2e8.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/az.d656062b0f.js +0 -1
- package/dist/client/static/js/async/b.b4f6f06515.js +0 -1
- package/dist/client/static/js/async/bi.09688ba75c.js +0 -1
- package/dist/client/static/js/async/bq.586bc738d1.js +0 -1
- package/dist/client/static/js/async/c.797d933882.js +0 -1
- package/dist/client/static/js/async/c0.0af1e640d3.js +0 -24
- package/dist/client/static/js/async/ca.8a52cfd447.js +0 -1
- package/dist/client/static/js/async/cf.d34fff5d67.js +0 -1
- package/dist/client/static/js/async/ct.b74928f7a3.js +0 -1
- package/dist/client/static/js/async/cx.3cb0242a3a.js +0 -226
- package/dist/client/static/js/async/d.a5fc8fe1b3.js +0 -1
- package/dist/client/static/js/async/da.f62c70f6c8.js +0 -1
- package/dist/client/static/js/async/dp.1725d6432c.js +0 -1
- package/dist/client/static/js/async/e.93214d622b.js +0 -79
- package/dist/client/static/js/async/f.ab50467719.js +0 -19
- package/dist/client/static/js/async/f.ab50467719.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/f7.46971e9d6d.js +0 -2
- package/dist/client/static/js/async/f7.46971e9d6d.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/f8.bbf0dbae70.js +0 -1
- package/dist/client/static/js/async/g.cb81980498.js +0 -2
- package/dist/client/static/js/async/g.cb81980498.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/h.4e790471e4.js +0 -27
- package/dist/client/static/js/async/h1.393f066d09.js +0 -1
- package/dist/client/static/js/async/h3.216e9798ab.js +0 -148
- package/dist/client/static/js/async/hd.0da931b622.js +0 -1
- package/dist/client/static/js/async/hk.ba7fb04d41.js +0 -1
- package/dist/client/static/js/async/hn.b645041e00.js +0 -1
- package/dist/client/static/js/async/ho.475a5e1ae4.js +0 -1
- package/dist/client/static/js/async/hp.e9fe2e395a.js +0 -2
- package/dist/client/static/js/async/hp.e9fe2e395a.js.LICENSE.txt +0 -17
- package/dist/client/static/js/async/hs.e7c59d8090.js +0 -41
- package/dist/client/static/js/async/i.7c148e8440.js +0 -1
- package/dist/client/static/js/async/id.573df20f29.js +0 -1
- package/dist/client/static/js/async/j.ab925882d8.js +0 -1
- package/dist/client/static/js/async/jd.63886a1f09.js +0 -1
- package/dist/client/static/js/async/jg.d86ebd4013.js +0 -86
- package/dist/client/static/js/async/jl.874d352278.js +0 -1
- package/dist/client/static/js/async/jll.11313b455a.js +0 -1
- package/dist/client/static/js/async/js.67182759ef.js +0 -1
- package/dist/client/static/js/async/jsc.23b70d6f40.js +0 -1
- package/dist/client/static/js/async/jse.334588ff5d.js +0 -1
- package/dist/client/static/js/async/jz.72eb929106.js +0 -77
- package/dist/client/static/js/async/k.3210286401.js +0 -217
- package/dist/client/static/js/async/ka.c1990ecaa3.js +0 -115
- package/dist/client/static/js/async/katex-vendor.de6a87c308.js +0 -1
- package/dist/client/static/js/async/ku.29c8a59d0c.js +0 -1
- package/dist/client/static/js/async/ky.8dab1dade0.js +0 -1
- package/dist/client/static/js/async/l.12dd34e1be.js +0 -1
- package/dist/client/static/js/async/l9.949710adaf.js +0 -83
- package/dist/client/static/js/async/l9.949710adaf.js.LICENSE.txt +0 -13
- package/dist/client/static/js/async/ls.9d9a01026c.js +0 -104
- package/dist/client/static/js/async/m.eb2e6752e2.js +0 -28
- package/dist/client/static/js/async/m.eb2e6752e2.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/m3.d2b05f9604.js +0 -2
- package/dist/client/static/js/async/m3.d2b05f9604.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/ma.2c261b04dd.js +0 -2
- package/dist/client/static/js/async/ma.2c261b04dd.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/me.8ace414f80.js +0 -23
- package/dist/client/static/js/async/ms.98960af4eb.js +0 -67
- package/dist/client/static/js/async/ms.98960af4eb.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/mt.0352e976d8.js +0 -1
- package/dist/client/static/js/async/n.a9f1c51701.js +0 -1
- package/dist/client/static/js/async/na.a36cd532d6.js +0 -1
- package/dist/client/static/js/async/nk.fc7e58325e.js +0 -1
- package/dist/client/static/js/async/o.36353dfb88.js +0 -1
- package/dist/client/static/js/async/oc.b73042e8bd.js +0 -8
- package/dist/client/static/js/async/og.8eb577893a.js +0 -193
- package/dist/client/static/js/async/p.48ecfcd14e.js +0 -1
- package/dist/client/static/js/async/px.fc6877924b.js +0 -1
- package/dist/client/static/js/async/q.22588845a9.js +0 -21
- package/dist/client/static/js/async/q.22588845a9.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/q5.97dc1481b6.js +0 -286
- package/dist/client/static/js/async/qs.2a9ddb3c72.js +0 -1
- package/dist/client/static/js/async/r.51010ad87c.js +0 -1
- package/dist/client/static/js/async/rd.cb6bf495bb.js +0 -2
- package/dist/client/static/js/async/rd.cb6bf495bb.js.LICENSE.txt +0 -4
- package/dist/client/static/js/async/s.0f395cdda5.js +0 -94
- package/dist/client/static/js/async/t.4dca0e2ba8.js +0 -1
- package/dist/client/static/js/async/t9.d44f57d518.js +0 -1
- package/dist/client/static/js/async/ta.973534bbec.js +0 -1
- package/dist/client/static/js/async/tr.e878658140.js +0 -309
- package/dist/client/static/js/async/u.0871ac5b22.js +0 -1
- package/dist/client/static/js/async/ud.3065bfee31.js +0 -2
- package/dist/client/static/js/async/ud.3065bfee31.js.LICENSE.txt +0 -4
- package/dist/client/static/js/async/up.ea990da0ff.js +0 -32
- package/dist/client/static/js/async/uw.8890abbabc.js +0 -1
- package/dist/client/static/js/async/v.7d28d3c241.js +0 -34
- package/dist/client/static/js/async/vp.9b13bf8610.js +0 -1
- package/dist/client/static/js/async/vt.42416b707d.js +0 -1
- package/dist/client/static/js/async/w.08ac210f60.js +0 -1
- package/dist/client/static/js/async/wt.ad92393a90.js +0 -1
- package/dist/client/static/js/async/x.563f72ca6b.js +0 -1
- package/dist/client/static/js/async/x0.475516418f.js +0 -1
- package/dist/client/static/js/async/x1.da8687159b.js +0 -90
- package/dist/client/static/js/async/xe.da8dcc2015.js +0 -2
- package/dist/client/static/js/async/xe.da8dcc2015.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/xl.a3c562a58c.js +0 -1
- package/dist/client/static/js/async/xn.6510c0abe2.js +0 -2
- package/dist/client/static/js/async/xn.6510c0abe2.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/xn1.83a7784fe1.js +0 -10
- package/dist/client/static/js/async/xn1.83a7784fe1.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/xnl.feb0021e50.js +0 -2
- package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/y.1a9ed7a5d8.js +0 -1
- package/dist/client/static/js/async/y6.43c57defe4.js +0 -1
- package/dist/client/static/js/async/yb.8c1342401d.js +0 -1
- package/dist/client/static/js/async/ybq.4491f2f2dc.js +0 -1
- package/dist/client/static/js/async/yg.35bebd6d0c.js +0 -149
- package/dist/client/static/js/async/yq.917ddd7ba2.js +0 -1
- package/dist/client/static/js/async/ys.00687cf82d.js +0 -38
- package/dist/client/static/js/async/yv.d99d829136.js +0 -1
- package/dist/client/static/js/async/z.56f61a4374.js +0 -1
- package/dist/client/static/js/async/zi.5fdb07b86a.js +0 -24
- package/dist/client/static/js/async/zj.654f37e8ff.js +0 -1
- package/dist/client/static/js/async/zn.966d6f1cc6.js +0 -1
- package/dist/client/static/js/async/zo.26213b40c0.js +0 -1
- package/dist/client/static/js/async/zx.e9f4105e00.js +0 -1
- package/dist/client/static/js/index.e6a257402a.js +0 -38
- package/dist/client/static/js/index.e6a257402a.js.LICENSE.txt +0 -4
- package/dist/client/static/js/markdown-vendor.71e43278b3.js +0 -144
- package/dist/client/static/js/shiki-vendor.cb8452ddcd.js +0 -1
- package/src/client/components/settings/categories/provider-settings/Icons.tsx +0 -35
- package/src/client/hooks/settings/providers.ts +0 -326
- package/src/server/plugins/dev-assets.ts +0 -49
- package/src/shared/lib/code/editor-utils.ts +0 -16
- package/src/shared/lib/models/provider-presets.ts +0 -29
- /package/dist/client/{apple-touch-icon.png → static/apple-touch-icon-dskx5sr3.png} +0 -0
- /package/dist/client/{icon-192x192.png → static/icon-192x192-6gn5cr2f.png} +0 -0
- /package/dist/client/{icon.svg → static/icon-d07z9dtz.svg} +0 -0
- /package/dist/client/{manifest.webmanifest → static/manifest-bnyy75kt.webmanifest} +0 -0
- /package/{dist/client → public}/icon-512x512.png +0 -0
- /package/{dist/client → public}/icon-maskable-512x512.png +0 -0
- /package/{dist/client → public}/sw.js +0 -0
- /package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.ts +0 -0
- /package/src/shared/lib/models/{provider-cleanup.ts → provider/cleanup.ts} +0 -0
- /package/src/shared/lib/models/{provider-label.ts → provider/label.ts} +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Provider spelling -> brand glyph, plus the initials fallback for a provider
|
|
8
|
+
* that publishes no logo.
|
|
9
|
+
*
|
|
10
|
+
* A provider reaches this module as whatever string omp reported: a login
|
|
11
|
+
* provider id (`google-vertex`), a models.yml slug the user typed
|
|
12
|
+
* (`my-gateway`), a legacy chamber icon key (`claude`), or a display name
|
|
13
|
+
* (`DeepSeek`). All four must land on the same mark, so lookup folds the input
|
|
14
|
+
* to lowercase alphanumerics and matches it against this table.
|
|
15
|
+
*
|
|
16
|
+
* The table is DATA, not a chain of `if`s: `minimax-code-cn` is the same brand
|
|
17
|
+
* as `minimax`, and a new variant belongs here as one more row rather than as
|
|
18
|
+
* another branch at every call site.
|
|
19
|
+
*
|
|
20
|
+
* Pure and dependency-free so the server (which builds provider rows) and the
|
|
21
|
+
* client (which renders them) resolve a provider identically.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalised provider spelling -> glyph name in `PROVIDER_GLYPHS`.
|
|
26
|
+
*
|
|
27
|
+
* Keys are lowercase alphanumerics only (`google-vertex` is `googlevertex`).
|
|
28
|
+
* Values name an entry in the glyph table, or a brand whose mark stands for
|
|
29
|
+
* several providers (Bedrock draws Amazon's mark).
|
|
30
|
+
*/
|
|
31
|
+
export const PROVIDER_GLYPH_ALIASES: Readonly<Record<string, string>> = {
|
|
32
|
+
abliteration: 'abliteration',
|
|
33
|
+
aiand: 'aiand',
|
|
34
|
+
aimlapi: 'aimlapi',
|
|
35
|
+
alibaba: 'alibaba',
|
|
36
|
+
alibabacloud: 'alibaba',
|
|
37
|
+
alibabacodingplan: 'alibaba',
|
|
38
|
+
alibabatokenplan: 'alibaba',
|
|
39
|
+
amazon: 'amazon',
|
|
40
|
+
amazonbedrock: 'amazon',
|
|
41
|
+
anthropic: 'anthropic',
|
|
42
|
+
antigravity: 'antigravity',
|
|
43
|
+
apple: 'apple',
|
|
44
|
+
aws: 'amazon',
|
|
45
|
+
azure: 'azure',
|
|
46
|
+
azureai: 'azure',
|
|
47
|
+
baidu: 'baidu',
|
|
48
|
+
baseten: 'baseten',
|
|
49
|
+
bedrock: 'amazon',
|
|
50
|
+
bedrockmantle: 'amazon',
|
|
51
|
+
cerebras: 'cerebras',
|
|
52
|
+
charm: 'charm',
|
|
53
|
+
charmhyper: 'charm',
|
|
54
|
+
chatglm: 'zai',
|
|
55
|
+
claude: 'claude',
|
|
56
|
+
claudecode: 'claude',
|
|
57
|
+
cline: 'cline',
|
|
58
|
+
clinepass: 'cline',
|
|
59
|
+
cloudflare: 'cloudflare',
|
|
60
|
+
cloudflareaigateway: 'cloudflare',
|
|
61
|
+
codex: 'codex',
|
|
62
|
+
commandcode: 'commandcode',
|
|
63
|
+
copilot: 'githubcopilot',
|
|
64
|
+
coreweave: 'coreweave',
|
|
65
|
+
cursor: 'cursor',
|
|
66
|
+
deepinfra: 'deepinfra',
|
|
67
|
+
deepseek: 'deepseek',
|
|
68
|
+
devin: 'devin',
|
|
69
|
+
ernie: 'baidu',
|
|
70
|
+
firepass: 'fireworks',
|
|
71
|
+
fireworks: 'fireworks',
|
|
72
|
+
gemini: 'gemini',
|
|
73
|
+
geminicli: 'geminicli',
|
|
74
|
+
githubcopilot: 'githubcopilot',
|
|
75
|
+
gitlab: 'gitlab',
|
|
76
|
+
gitlabduo: 'gitlab',
|
|
77
|
+
gitlabduoagent: 'gitlab',
|
|
78
|
+
glm: 'zai',
|
|
79
|
+
gmicloud: 'gmicloud',
|
|
80
|
+
google: 'google',
|
|
81
|
+
googleantigravity: 'antigravity',
|
|
82
|
+
googlecloud: 'google',
|
|
83
|
+
googlegemini: 'gemini',
|
|
84
|
+
googlegeminicli: 'geminicli',
|
|
85
|
+
googlevertex: 'vertexai',
|
|
86
|
+
grok: 'xai',
|
|
87
|
+
groq: 'groq',
|
|
88
|
+
huggingface: 'huggingface',
|
|
89
|
+
kenari: 'kenari',
|
|
90
|
+
kilo: 'kilocode',
|
|
91
|
+
kilocode: 'kilocode',
|
|
92
|
+
kimi: 'kimi',
|
|
93
|
+
kimicode: 'kimi',
|
|
94
|
+
litellm: 'litellm',
|
|
95
|
+
llama: 'meta',
|
|
96
|
+
llamacpp: 'llamacpp',
|
|
97
|
+
lmstudio: 'lmstudio',
|
|
98
|
+
meta: 'meta',
|
|
99
|
+
metaai: 'metaai',
|
|
100
|
+
microsoftazure: 'azure',
|
|
101
|
+
minimax: 'minimax',
|
|
102
|
+
minimaxcode: 'minimax',
|
|
103
|
+
minimaxcodecn: 'minimax',
|
|
104
|
+
mistral: 'mistral',
|
|
105
|
+
mistralai: 'mistral',
|
|
106
|
+
moonshot: 'moonshot',
|
|
107
|
+
moonshotai: 'moonshot',
|
|
108
|
+
musecode: 'metaai',
|
|
109
|
+
nanogpt: 'nanogpt',
|
|
110
|
+
novita: 'novita',
|
|
111
|
+
nvidia: 'nvidia',
|
|
112
|
+
ollama: 'ollama',
|
|
113
|
+
ollamacloud: 'ollama',
|
|
114
|
+
openai: 'openai',
|
|
115
|
+
openaicodex: 'codex',
|
|
116
|
+
opencode: 'opencode',
|
|
117
|
+
opencodego: 'opencode',
|
|
118
|
+
opencodezen: 'opencode',
|
|
119
|
+
openrouter: 'openrouter',
|
|
120
|
+
qianfan: 'baidu',
|
|
121
|
+
qwen: 'qwen',
|
|
122
|
+
qwenportal: 'qwen',
|
|
123
|
+
sakana: 'sakana',
|
|
124
|
+
sakanaai: 'sakana',
|
|
125
|
+
siliconcloud: 'siliconcloud',
|
|
126
|
+
siliconflow: 'siliconcloud',
|
|
127
|
+
siliconflowcn: 'siliconcloud',
|
|
128
|
+
singularity: 'singularity',
|
|
129
|
+
singularityapidev: 'singularity',
|
|
130
|
+
singularityapitech: 'singularity',
|
|
131
|
+
stepfun: 'stepfun',
|
|
132
|
+
synthetic: 'synthetic',
|
|
133
|
+
together: 'together',
|
|
134
|
+
togetherai: 'together',
|
|
135
|
+
venice: 'venice',
|
|
136
|
+
vercel: 'vercel',
|
|
137
|
+
vercelaigateway: 'vercel',
|
|
138
|
+
vertexai: 'vertexai',
|
|
139
|
+
vllm: 'vllm',
|
|
140
|
+
wafer: 'wafer',
|
|
141
|
+
waferserverless: 'wafer',
|
|
142
|
+
xai: 'xai',
|
|
143
|
+
xaioauth: 'xai',
|
|
144
|
+
xiaomi: 'xiaomi',
|
|
145
|
+
xiaomitokenplanams: 'xiaomi',
|
|
146
|
+
xiaomitokenplancn: 'xiaomi',
|
|
147
|
+
xiaomitokenplansgp: 'xiaomi',
|
|
148
|
+
yolo: 'yoloauto',
|
|
149
|
+
yoloauto: 'yoloauto',
|
|
150
|
+
zai: 'zai',
|
|
151
|
+
zaicodingplan: 'zai',
|
|
152
|
+
openaicodexdevice: 'codex',
|
|
153
|
+
perplexity: 'perplexity',
|
|
154
|
+
tavily: 'tavily',
|
|
155
|
+
kagi: 'kagi',
|
|
156
|
+
exa: 'exa',
|
|
157
|
+
stencil: 'stencil',
|
|
158
|
+
zenmux: 'zenmux',
|
|
159
|
+
zhipu: 'zhipu',
|
|
160
|
+
zhipucodingplan: 'zhipu',
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The glyph name for a provider, or `null` when no brand mark is registered —
|
|
165
|
+
* the caller then draws the initials badge instead of an empty box.
|
|
166
|
+
*
|
|
167
|
+
* Candidates are tried in order and each is folded to lowercase alphanumerics
|
|
168
|
+
* first, so a stored legacy icon key (`claude`) still resolves after the
|
|
169
|
+
* provider's slug changes. Pass the provider's own `icon` field before its slug
|
|
170
|
+
* to keep that precedence.
|
|
171
|
+
*/
|
|
172
|
+
export function resolveProviderGlyph(
|
|
173
|
+
...candidates: Array<string | null | undefined>
|
|
174
|
+
): string | null {
|
|
175
|
+
for (const candidate of candidates) {
|
|
176
|
+
if (!candidate) continue;
|
|
177
|
+
const key = candidate.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
178
|
+
const alias = key ? PROVIDER_GLYPH_ALIASES[key] : undefined;
|
|
179
|
+
if (alias) return alias;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Fallback badge text for a provider with no brand mark: the initial of each of
|
|
186
|
+
* the label's first two title-cased words, joined and closed with a single dot —
|
|
187
|
+
* `Tooker` -> `t.`, `Toktok ID` -> `ti.`.
|
|
188
|
+
*
|
|
189
|
+
* Lowercase because the badge renders beside lowercase UI labels, and capped at
|
|
190
|
+
* two initials because a third overflows the badge at icon size.
|
|
191
|
+
*/
|
|
192
|
+
export function providerInitials(label: string): string {
|
|
193
|
+
const words = label.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
194
|
+
if (words.length === 0) return '?';
|
|
195
|
+
const initials = words
|
|
196
|
+
.slice(0, 2)
|
|
197
|
+
.map((word) => word.charAt(0).toLowerCase())
|
|
198
|
+
.join('');
|
|
199
|
+
return `${initials}.`;
|
|
200
|
+
}
|
|
@@ -5,26 +5,49 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ProviderModel } from '@/shared/types';
|
|
8
|
+
import type {
|
|
9
|
+
OmpProviderApi,
|
|
10
|
+
ProviderAuthMode,
|
|
11
|
+
ProviderDiscoveryType,
|
|
12
|
+
} from '@/shared/types/settings/provider';
|
|
13
|
+
import type { ProviderThinkingEffort } from '@/shared/lib/models/provider/dialect';
|
|
8
14
|
import { notifyModelsUpdated } from '@/shared/lib/models/client';
|
|
9
15
|
|
|
10
16
|
export interface ProviderModelsFetchResult {
|
|
11
17
|
ok: boolean;
|
|
12
18
|
models?: ProviderModel[];
|
|
13
19
|
error?: string;
|
|
14
|
-
omp?: {
|
|
20
|
+
omp?: {
|
|
21
|
+
written: boolean;
|
|
22
|
+
addedCount: number;
|
|
23
|
+
backfilledCount: number;
|
|
24
|
+
reason?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ProviderModelsFetchRequest {
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
apiKey?: string;
|
|
31
|
+
providerSlug?: string;
|
|
32
|
+
persistToOmp?: boolean;
|
|
33
|
+
/** Wire dialect to probe and to write into models.yml. */
|
|
34
|
+
api?: OmpProviderApi;
|
|
35
|
+
/** omp auth mode; `none` marks a keyless local server. */
|
|
36
|
+
auth?: ProviderAuthMode;
|
|
37
|
+
/** Register the provider for omp's own live discovery — no listing here. */
|
|
38
|
+
discovery?: ProviderDiscoveryType;
|
|
39
|
+
/** Register the provider WITHOUT listing models (endpoint override only). */
|
|
40
|
+
registerOnly?: boolean;
|
|
15
41
|
}
|
|
16
42
|
|
|
17
43
|
export async function fetchProviderModelsRemote(
|
|
18
|
-
|
|
19
|
-
apiKey?: string,
|
|
20
|
-
providerSlug?: string,
|
|
21
|
-
persistToOmp = false,
|
|
44
|
+
request: ProviderModelsFetchRequest,
|
|
22
45
|
): Promise<ProviderModelsFetchResult> {
|
|
23
46
|
try {
|
|
24
47
|
const response = await fetch('/api/settings/provider-models', {
|
|
25
48
|
method: 'POST',
|
|
26
49
|
headers: { 'Content-Type': 'application/json' },
|
|
27
|
-
body: JSON.stringify(
|
|
50
|
+
body: JSON.stringify(request),
|
|
28
51
|
});
|
|
29
52
|
const data = await response.json() as ProviderModelsFetchResult;
|
|
30
53
|
if (!response.ok) {
|
|
@@ -45,6 +68,68 @@ export interface ProviderModelsMergeResult {
|
|
|
45
68
|
addedCount: number;
|
|
46
69
|
}
|
|
47
70
|
|
|
71
|
+
/** One model registered by hand — the Add Model dialog's payload. */
|
|
72
|
+
export interface ManualModelRequest {
|
|
73
|
+
provider: string;
|
|
74
|
+
id: string;
|
|
75
|
+
name?: string;
|
|
76
|
+
/** Token counts, not the compact labels a listing produces. */
|
|
77
|
+
contextWindow?: number;
|
|
78
|
+
maxTokens?: number;
|
|
79
|
+
reasoning?: boolean;
|
|
80
|
+
imageInput?: boolean;
|
|
81
|
+
/** USD per 1M tokens; both input and output are needed for a cost to be written. */
|
|
82
|
+
costInput?: number;
|
|
83
|
+
costOutput?: number;
|
|
84
|
+
costCacheRead?: number;
|
|
85
|
+
costCacheWrite?: number;
|
|
86
|
+
/** Reasoning ladder this model accepts; omitted means "leave omp's default". */
|
|
87
|
+
efforts?: ProviderThinkingEffort[];
|
|
88
|
+
/** Only used when the provider has no models.yml entry yet. */
|
|
89
|
+
baseUrl?: string;
|
|
90
|
+
apiKey?: string;
|
|
91
|
+
auth?: ProviderAuthMode;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ManualModelResult {
|
|
95
|
+
success: boolean;
|
|
96
|
+
written: boolean;
|
|
97
|
+
addedModels?: string[];
|
|
98
|
+
backfilledModels?: string[];
|
|
99
|
+
/** The provider already registers this id; nothing was overwritten. */
|
|
100
|
+
alreadyKnown?: boolean;
|
|
101
|
+
reason?: string;
|
|
102
|
+
error?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Register one model in models.yml by hand. Unlike the auto-fetch this does not
|
|
107
|
+
* need a listing endpoint — the id and its metadata come from the user — so it
|
|
108
|
+
* is the path for a gateway model its `/models` route omits, an Azure
|
|
109
|
+
* deployment name, or any provider omp cannot enumerate.
|
|
110
|
+
*/
|
|
111
|
+
export async function addProviderModel(request: ManualModelRequest): Promise<ManualModelResult> {
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch('/api/settings/provider-model', {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
body: JSON.stringify(request),
|
|
117
|
+
});
|
|
118
|
+
const data = await response.json() as ManualModelResult;
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
return { ...data, success: false, written: false, error: data?.error || data?.reason };
|
|
121
|
+
}
|
|
122
|
+
notifyModelsUpdated();
|
|
123
|
+
return data;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
written: false,
|
|
128
|
+
error: error instanceof Error ? error.message : 'Network error while adding the model',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
48
133
|
/**
|
|
49
134
|
* Existing entries pass through untouched (visibility/config survive); the
|
|
50
135
|
* chat model catalog is synced from the newly added entries only.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The Add Provider picker's rules, which are all silent when wrong: a preset
|
|
8
|
+
* offered for a CONNECTED provider produces a second `models.yml` entry under
|
|
9
|
+
* the same slug (the upsert then writes into the live one), and a preset
|
|
10
|
+
* dropped while its provider is only DISCONNECTED makes the switch that hid it
|
|
11
|
+
* unreachable. The dialect fields are checked too — a preset whose `api` omp
|
|
12
|
+
* does not know would disable every custom provider in the file.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, expect, test } from 'bun:test';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
buildAvailableProviderPresets,
|
|
19
|
+
configuredProviderSlugs,
|
|
20
|
+
groupProviderPresets,
|
|
21
|
+
} from '@/shared/lib/models/provider/presets';
|
|
22
|
+
import { PROVIDER_APIS, providerEmptyReason } from '@/shared/lib/models/provider/dialect';
|
|
23
|
+
import { PRESET_NEW_PROVIDERS } from '@/client/data/settings/provider';
|
|
24
|
+
import type { PresetProviderOption, ProviderItem } from '@/shared/types/settings/provider';
|
|
25
|
+
|
|
26
|
+
function provider(overrides: Partial<ProviderItem> & { slug: string }): ProviderItem {
|
|
27
|
+
return {
|
|
28
|
+
id: `id-${overrides.slug}`,
|
|
29
|
+
name: overrides.slug,
|
|
30
|
+
icon: overrides.slug,
|
|
31
|
+
status: 'connected',
|
|
32
|
+
configuredIn: 'models.yml',
|
|
33
|
+
models: [],
|
|
34
|
+
...overrides,
|
|
35
|
+
} as ProviderItem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const PRESETS: PresetProviderOption[] = [
|
|
39
|
+
{ id: 'openai', name: 'OpenAI', slug: 'openai', icon: 'openai', defaultUrl: 'https://api.openai.com/v1', group: 'Frontier APIs' },
|
|
40
|
+
{ id: 'groq', name: 'Groq', slug: 'groq', icon: 'groq', defaultUrl: 'https://api.groq.com/openai/v1', group: 'Frontier APIs' },
|
|
41
|
+
{ id: 'ollama', name: 'Ollama', slug: 'ollama', icon: 'ollama', defaultUrl: 'http://127.0.0.1:11434', auth: 'none', discovery: 'ollama', group: 'Local & self-hosted' },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
describe('buildAvailableProviderPresets', () => {
|
|
45
|
+
test('drops a non-bundled preset whose slug already has an endpoint', () => {
|
|
46
|
+
// A second entry under one slug is the same entry to omp; that provider is
|
|
47
|
+
// managed from the sidebar instead.
|
|
48
|
+
const available = buildAvailableProviderPresets(
|
|
49
|
+
[provider({ slug: 'openai', baseUrl: 'https://api.openai.com/v1' })],
|
|
50
|
+
PRESETS,
|
|
51
|
+
);
|
|
52
|
+
expect(available.map((entry) => entry.slug)).toEqual(['groq', 'ollama']);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('keeps a preset whose provider omp merely KNOWS', () => {
|
|
56
|
+
// omp reports ~60 providers it knows with no endpoint and no credential;
|
|
57
|
+
// treating those as configured hid every gateway preset — the one case
|
|
58
|
+
// where the user wants to bring their own key.
|
|
59
|
+
const available = buildAvailableProviderPresets(
|
|
60
|
+
[provider({ slug: 'openai', status: 'disconnected', disabled: true })],
|
|
61
|
+
PRESETS,
|
|
62
|
+
);
|
|
63
|
+
expect(available.map((entry) => entry.slug)).toEqual(['openai', 'groq', 'ollama']);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('keeps a bundled preset even when its provider is configured', () => {
|
|
67
|
+
// An override for a provider omp already ships is the documented way to
|
|
68
|
+
// point it at a proxy, and the writer is add-only, so re-offering is safe.
|
|
69
|
+
const available = buildAvailableProviderPresets(
|
|
70
|
+
[provider({ slug: 'azure', status: 'connected', baseUrl: 'https://x.openai.azure.com' })],
|
|
71
|
+
[{ ...PRESETS[0], slug: 'azure', id: 'azure', bundled: true }],
|
|
72
|
+
);
|
|
73
|
+
expect(available.map((entry) => entry.slug)).toEqual(['azure']);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('never offers one slug twice', () => {
|
|
77
|
+
const available = buildAvailableProviderPresets(
|
|
78
|
+
[provider({ slug: 'ollama', status: 'disconnected' })],
|
|
79
|
+
PRESETS,
|
|
80
|
+
);
|
|
81
|
+
const slugs = available.map((entry) => entry.slug);
|
|
82
|
+
expect(new Set(slugs).size).toBe(slugs.length);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('groupProviderPresets', () => {
|
|
87
|
+
test('buckets by group in declaration order and keeps members in order', () => {
|
|
88
|
+
const groups = groupProviderPresets(PRESETS);
|
|
89
|
+
expect(groups.map((entry) => entry.group)).toEqual(['Frontier APIs', 'Local & self-hosted']);
|
|
90
|
+
expect(groups[0].options.map((entry) => entry.slug)).toEqual(['openai', 'groq']);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('shipped presets', () => {
|
|
95
|
+
test('every declared api is one omp accepts', () => {
|
|
96
|
+
for (const preset of PRESET_NEW_PROVIDERS) {
|
|
97
|
+
if (!preset.api) continue;
|
|
98
|
+
expect(PROVIDER_APIS).toContain(preset.api);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('every preset carries a group and a unique id', () => {
|
|
103
|
+
const ids = PRESET_NEW_PROVIDERS.map((preset) => preset.id);
|
|
104
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
105
|
+
for (const preset of PRESET_NEW_PROVIDERS) {
|
|
106
|
+
expect(preset.group.length).toBeGreaterThan(0);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('a discovery preset declares a keyless or authenticated shape, never neither', () => {
|
|
111
|
+
// omp needs a dialect for every discovery provider except `proxy`, which
|
|
112
|
+
// reports one per model; a preset without either would be rejected on load.
|
|
113
|
+
for (const preset of PRESET_NEW_PROVIDERS) {
|
|
114
|
+
if (!preset.discovery) continue;
|
|
115
|
+
expect(preset.api !== undefined || preset.discovery === 'proxy').toBe(true);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('a built-in local engine preset is keyless', () => {
|
|
120
|
+
// omp's implicit ollama/llama.cpp/lm-studio engines are keyless; a preset
|
|
121
|
+
// that omitted `auth: none` would be registered as a provider omp reports
|
|
122
|
+
// as unconfigured. LiteLLM is deliberately excluded — it is a proxy that
|
|
123
|
+
// authenticates with its own master key.
|
|
124
|
+
const engines = PRESET_NEW_PROVIDERS.filter(
|
|
125
|
+
(preset) => preset.discovery === 'ollama'
|
|
126
|
+
|| preset.discovery === 'llama.cpp'
|
|
127
|
+
|| preset.discovery === 'lm-studio',
|
|
128
|
+
);
|
|
129
|
+
expect(engines.length).toBe(3);
|
|
130
|
+
for (const preset of engines) {
|
|
131
|
+
expect(preset.auth).toBe('none');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('configuredProviderSlugs', () => {
|
|
137
|
+
test('names only the providers that already own a slug', () => {
|
|
138
|
+
// omp reports ~60 providers it merely knows; naming those as taken would
|
|
139
|
+
// block the Add Provider dialog from ever configuring one of them.
|
|
140
|
+
const slugs = configuredProviderSlugs([
|
|
141
|
+
provider({ slug: 'openai', status: 'disconnected' }),
|
|
142
|
+
provider({ slug: 'azure', status: 'disconnected', baseUrl: 'https://x.openai.azure.com' }),
|
|
143
|
+
provider({ slug: 'deepseek' }),
|
|
144
|
+
]);
|
|
145
|
+
expect(slugs).toEqual(['azure', 'deepseek']);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('providerEmptyReason', () => {
|
|
150
|
+
test('explains a discovery provider without calling it empty', () => {
|
|
151
|
+
expect(providerEmptyReason({ discovery: 'lm-studio' })).toContain('discovers this provider');
|
|
152
|
+
expect(providerEmptyReason({ modelSource: 'discovery' })).toContain('discovers this provider');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('an override names the missing credential rather than only the shape', () => {
|
|
156
|
+
const withKey = providerEmptyReason({ modelSource: 'override' });
|
|
157
|
+
expect(withKey).toContain('Endpoint override');
|
|
158
|
+
expect(withKey).toContain('still needs a credential');
|
|
159
|
+
|
|
160
|
+
// A keyless override has nothing missing, so it must not claim one is.
|
|
161
|
+
const keyless = providerEmptyReason({ modelSource: 'override', auth: 'none' });
|
|
162
|
+
expect(keyless).toContain('Endpoint override');
|
|
163
|
+
expect(keyless).not.toContain('still needs a credential');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('an unexplained empty list stays unexplained', () => {
|
|
167
|
+
// A hosted provider with no models is a problem; inventing a reassuring
|
|
168
|
+
// reason for it would hide the failure.
|
|
169
|
+
expect(providerEmptyReason({})).toBeUndefined();
|
|
170
|
+
expect(providerEmptyReason({ modelSource: 'fetch' })).toBeUndefined();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Which presets the Add Provider picker may offer, and how they are grouped.
|
|
8
|
+
*
|
|
9
|
+
* The rule differs by preset kind, because the two kinds write different things:
|
|
10
|
+
*
|
|
11
|
+
* - A **bundled** preset names a provider omp already ships (Anthropic, Azure,
|
|
12
|
+
* Bedrock, Vertex, Codex). Its entry in `models.yml` is an OVERRIDE — omp
|
|
13
|
+
* keeps serving its own model list and only the endpoint changes — and the
|
|
14
|
+
* writer is add-only, so re-offering it is safe and is the documented way to
|
|
15
|
+
* point a built-in provider at a proxy.
|
|
16
|
+
* - Any other preset creates a provider entry of its own, so a slug that is
|
|
17
|
+
* already DEFINED — connected, or carrying its own `baseUrl` in models.yml or
|
|
18
|
+
* the chamber overlay — is not re-offered; that provider is managed from the
|
|
19
|
+
* sidebar, which can reconnect it and edit its endpoint.
|
|
20
|
+
*
|
|
21
|
+
* `status` alone is the wrong test: omp reports ~60 providers it merely
|
|
22
|
+
* KNOWS (`get_login_providers`), which are `disconnected` with no endpoint and
|
|
23
|
+
* no credential. Treating those as "already added" hid every gateway preset —
|
|
24
|
+
* the one case where a user wants to bring their own key.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { PresetProviderOption, ProviderItem } from '@/shared/types/settings/provider';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* True when the provider already owns this slug in a config file — connected,
|
|
31
|
+
* or carrying its own `baseUrl` from models.yml or the chamber overlay.
|
|
32
|
+
*/
|
|
33
|
+
export function providerOwnsSlug(provider: ProviderItem | undefined): boolean {
|
|
34
|
+
return provider !== undefined && (provider.status === 'connected' || Boolean(provider.baseUrl));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Slugs already DEFINED in a config file — the ones a new provider entry would
|
|
39
|
+
* collide with. Deliberately not every provider omp reports: it lists ~60 it
|
|
40
|
+
* merely knows (`get_login_providers`), all `disconnected` with no endpoint, and
|
|
41
|
+
* treating those as taken blocks the user from ever configuring them.
|
|
42
|
+
*/
|
|
43
|
+
export function configuredProviderSlugs(providers: ProviderItem[]): string[] {
|
|
44
|
+
return providers
|
|
45
|
+
.filter((provider) => provider.slug.trim().length > 0 && providerOwnsSlug(provider))
|
|
46
|
+
.map((provider) => provider.slug);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function buildAvailableProviderPresets(
|
|
50
|
+
providers: ProviderItem[],
|
|
51
|
+
presets: PresetProviderOption[],
|
|
52
|
+
): PresetProviderOption[] {
|
|
53
|
+
const bySlug = new Map<string, ProviderItem>();
|
|
54
|
+
for (const provider of providers) {
|
|
55
|
+
const slug = provider.slug.trim().toLowerCase();
|
|
56
|
+
if (slug) bySlug.set(slug, provider);
|
|
57
|
+
}
|
|
58
|
+
const seenSlugs = new Set<string>();
|
|
59
|
+
const available: PresetProviderOption[] = [];
|
|
60
|
+
|
|
61
|
+
for (const preset of presets) {
|
|
62
|
+
const slug = preset.slug.trim().toLowerCase();
|
|
63
|
+
if (seenSlugs.has(slug)) continue;
|
|
64
|
+
if (!preset.bundled && providerOwnsSlug(bySlug.get(slug))) continue;
|
|
65
|
+
seenSlugs.add(slug);
|
|
66
|
+
available.push(preset);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return available;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Presets bucketed into their `group`, in the order the presets declare them. */
|
|
73
|
+
export function groupProviderPresets(
|
|
74
|
+
presets: PresetProviderOption[],
|
|
75
|
+
): Array<{ group: string; options: PresetProviderOption[] }> {
|
|
76
|
+
const groups: Array<{ group: string; options: PresetProviderOption[] }> = [];
|
|
77
|
+
for (const preset of presets) {
|
|
78
|
+
const existing = groups.find((entry) => entry.group === preset.group);
|
|
79
|
+
if (existing) existing.options.push(preset);
|
|
80
|
+
else groups.push({ group: preset.group, options: [preset] });
|
|
81
|
+
}
|
|
82
|
+
return groups;
|
|
83
|
+
}
|