ompchamber 2.0.2 → 3.0.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 +7 -6
- 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-prbmx3yj.js +5123 -0
- package/dist/client/static/js/index-ranztes9.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/provider-icon/glyphs.ts +118 -0
- package/src/client/components/common/provider-icon/index.tsx +85 -0
- package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +4 -0
- package/src/client/components/layout/desktop-layout/index.tsx +2 -0
- package/src/client/components/mobile/LayoutWrapper.tsx +16 -0
- 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/chat-timeline/AiMessageFooter.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/GeneratingIndicator.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/Tabs.tsx +2 -2
- package/src/client/components/workspace/editor/Toolbar.tsx +111 -26
- package/src/client/components/workspace/editor/index.tsx +27 -9
- 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/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/use-file-editor.ts +24 -27
- 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/stubs/elkjs.ts +6 -1
- 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/{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/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/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/workspace/file-tab-id.ts +29 -0
- package/src/shared/types/settings/provider.ts +81 -0
- package/src/shared/types/settings/usage.ts +88 -14
- 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/shared/lib/models/{provider-cleanup.ts → provider/cleanup.ts} +0 -0
- /package/src/shared/lib/models/{provider-label.ts → provider/label.ts} +0 -0
|
@@ -9,6 +9,10 @@ import { ReconnectModal } from '@/client/components/settings/categories/provider
|
|
|
9
9
|
import { LoginModal } from '@/client/components/settings/categories/provider-settings/LoginModal';
|
|
10
10
|
import { ModelConfigModal } from '@/client/components/settings/categories/provider-settings/ModelConfigModal';
|
|
11
11
|
import { ModelCapabilitiesModal } from '@/client/components/settings/categories/provider-settings/ModelCapabilitiesModal';
|
|
12
|
+
import { AddModelModal } from '@/client/components/settings/categories/provider-settings/AddModelModal';
|
|
13
|
+
import { DeleteProviderModal } from '@/client/components/settings/categories/provider-settings/DeleteProviderModal';
|
|
14
|
+
import { providerEmptyReason } from '@/shared/lib/models/provider/dialect';
|
|
15
|
+
import { configuredProviderSlugs } from '@/shared/lib/models/provider/presets';
|
|
12
16
|
|
|
13
17
|
interface ProviderSettingsProps {
|
|
14
18
|
autoOpenAdd?: boolean;
|
|
@@ -28,7 +32,15 @@ export function ProviderSettings({
|
|
|
28
32
|
setCurrentProject,
|
|
29
33
|
isFetchingModels,
|
|
30
34
|
toasts,
|
|
35
|
+
pushToast,
|
|
31
36
|
dismissToast,
|
|
37
|
+
isAddModelModalOpen,
|
|
38
|
+
setIsAddModelModalOpen,
|
|
39
|
+
handleModelAdded,
|
|
40
|
+
isDeleteModalOpen,
|
|
41
|
+
setIsDeleteModalOpen,
|
|
42
|
+
isDeletingProvider,
|
|
43
|
+
handleDeleteProvider,
|
|
32
44
|
isAddModalOpen,
|
|
33
45
|
setIsAddModalOpen,
|
|
34
46
|
isReconnectModalOpen,
|
|
@@ -72,19 +84,31 @@ export function ProviderSettings({
|
|
|
72
84
|
<ProviderAuthSection
|
|
73
85
|
provider={selectedProvider}
|
|
74
86
|
onOpenReconnectModal={() => {
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
// A provider omp signs in through `/login` needs the OAuth
|
|
88
|
+
// dialog; one whose endpoint and key live in models.yml (or the
|
|
89
|
+
// chamber overlay) is edited in place. Testing the provider ID
|
|
90
|
+
// for `omp-auth-` was wrong: a native models.yml entry shares
|
|
91
|
+
// that ID prefix once merged, and a keyless local server has no
|
|
92
|
+
// login flow to complete at all.
|
|
93
|
+
if (selectedProvider.credentialSource === 'omp-auth' && selectedProvider.auth !== 'none') {
|
|
94
|
+
setIsLoginModalOpen(true);
|
|
95
|
+
} else {
|
|
96
|
+
setIsReconnectModalOpen(true);
|
|
97
|
+
}
|
|
77
98
|
}}
|
|
78
99
|
onToggleDisconnect={handleToggleDisconnect}
|
|
79
100
|
onToggleDisabled={handleToggleProviderDisabled}
|
|
101
|
+
onDelete={() => setIsDeleteModalOpen(true)}
|
|
80
102
|
/>
|
|
81
103
|
|
|
82
104
|
<ProviderModelsList
|
|
83
105
|
models={selectedProvider.models}
|
|
106
|
+
emptyReason={providerEmptyReason(selectedProvider)}
|
|
84
107
|
onToggleModelVisibility={handleToggleModelVisibility}
|
|
85
108
|
onHideAll={handleHideAll}
|
|
86
109
|
onShowAll={handleShowAll}
|
|
87
110
|
onFetchModels={handleFetchModelsFromList}
|
|
111
|
+
onAddModel={() => setIsAddModelModalOpen(true)}
|
|
88
112
|
canFetchModels={Boolean(selectedProvider.baseUrl)}
|
|
89
113
|
isFetchingModels={isFetchingModels}
|
|
90
114
|
onOpenModelConfig={(model) => setConfigModel(model)}
|
|
@@ -108,7 +132,7 @@ export function ProviderSettings({
|
|
|
108
132
|
}}
|
|
109
133
|
onAddProvider={handleAddProvider}
|
|
110
134
|
presets={availablePresetProviders}
|
|
111
|
-
existingSlugs={providers
|
|
135
|
+
existingSlugs={configuredProviderSlugs(providers)}
|
|
112
136
|
/>
|
|
113
137
|
|
|
114
138
|
{selectedProvider && (
|
|
@@ -131,13 +155,30 @@ export function ProviderSettings({
|
|
|
131
155
|
/>
|
|
132
156
|
)}
|
|
133
157
|
|
|
158
|
+
{selectedProvider && (
|
|
159
|
+
<AddModelModal
|
|
160
|
+
isOpen={isAddModelModalOpen}
|
|
161
|
+
provider={selectedProvider}
|
|
162
|
+
onClose={() => setIsAddModelModalOpen(false)}
|
|
163
|
+
onAdded={(modelId) => void handleModelAdded(modelId)}
|
|
164
|
+
onError={(message) => pushToast(message, 'error')}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
<DeleteProviderModal
|
|
169
|
+
isOpen={isDeleteModalOpen}
|
|
170
|
+
provider={selectedProvider ?? null}
|
|
171
|
+
isDeleting={isDeletingProvider}
|
|
172
|
+
onClose={() => setIsDeleteModalOpen(false)}
|
|
173
|
+
onConfirm={() => void handleDeleteProvider()}
|
|
174
|
+
/>
|
|
175
|
+
|
|
134
176
|
<ModelConfigModal
|
|
135
177
|
isOpen={!!configModel}
|
|
136
178
|
model={configModel}
|
|
137
179
|
onClose={() => setConfigModel(null)}
|
|
138
180
|
onSaveModelConfig={handleSaveModelConfig}
|
|
139
181
|
/>
|
|
140
|
-
|
|
141
182
|
<ModelCapabilitiesModal
|
|
142
183
|
isOpen={!!capabilitiesModel}
|
|
143
184
|
model={capabilitiesModel}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { useState } from 'preact/hooks';
|
|
1
|
+
import { useEffect, useState } from 'preact/hooks';
|
|
2
2
|
import { AlertCircle, RefreshCw } from 'lucide-preact';
|
|
3
3
|
import { UsageSidebarList } from '@/client/components/settings/categories/usage-settings/SidebarList';
|
|
4
|
-
import type
|
|
4
|
+
import { buildProviders, type UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
|
|
5
5
|
import { ProviderDetail } from '@/client/components/settings/categories/usage-settings/ProviderDetail';
|
|
6
6
|
import { formatDateTime } from '@/client/components/settings/categories/usage-settings/format';
|
|
7
7
|
import { useUsageReport } from '@/client/hooks/settings/useUsageReport';
|
|
8
8
|
|
|
9
|
-
/** Settings → Usage: provider
|
|
9
|
+
/** Settings → Usage: provider quota, balances, and locally recorded burn. */
|
|
10
10
|
export function UsageSettings() {
|
|
11
11
|
const { report, isLoading, error, reload } = useUsageReport();
|
|
12
|
-
const [selectedProviderId, setSelectedProviderId] = useState<UsageProviderId>('
|
|
12
|
+
const [selectedProviderId, setSelectedProviderId] = useState<UsageProviderId>('');
|
|
13
|
+
|
|
14
|
+
const providers = report ? buildProviders(report) : [];
|
|
15
|
+
const selected = providers.find((provider) => provider.id === selectedProviderId) ?? providers[0];
|
|
16
|
+
|
|
17
|
+
// The report can arrive with a different provider set (a key was added or
|
|
18
|
+
// removed); keep the selection pointing at something that exists.
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (selected && selected.id !== selectedProviderId) setSelectedProviderId(selected.id);
|
|
21
|
+
}, [selected, selectedProviderId]);
|
|
13
22
|
|
|
14
23
|
if (error && !report) {
|
|
15
24
|
return (
|
|
@@ -39,7 +48,7 @@ export function UsageSettings() {
|
|
|
39
48
|
<div className="flex h-full w-full overflow-hidden bg-paper text-xs text-ink">
|
|
40
49
|
<UsageSidebarList
|
|
41
50
|
report={report}
|
|
42
|
-
selectedProviderId={
|
|
51
|
+
selectedProviderId={selected?.id ?? ''}
|
|
43
52
|
onSelectProvider={setSelectedProviderId}
|
|
44
53
|
/>
|
|
45
54
|
|
|
@@ -57,7 +66,7 @@ export function UsageSettings() {
|
|
|
57
66
|
</div>
|
|
58
67
|
<button
|
|
59
68
|
type="button"
|
|
60
|
-
onClick={() => reload()}
|
|
69
|
+
onClick={() => reload({ force: true })}
|
|
61
70
|
disabled={isLoading}
|
|
62
71
|
className="p-1.5 bg-paper border border-ink/15 rounded-md hover:bg-ink/5 text-ink/80 transition-colors disabled:opacity-50 flex-shrink-0"
|
|
63
72
|
title="Refresh usage"
|
|
@@ -69,7 +78,13 @@ export function UsageSettings() {
|
|
|
69
78
|
<div className="flex-1 overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static p-3.5 space-y-4">
|
|
70
79
|
{error && <p className="text-error text-xs">{error}</p>}
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
{selected ? (
|
|
82
|
+
<ProviderDetail summary={selected.summary} />
|
|
83
|
+
) : (
|
|
84
|
+
<p className="text-[11px] text-ink/50 leading-relaxed">
|
|
85
|
+
No provider credentials detected. Add an API key in Settings → Providers and it will appear here.
|
|
86
|
+
</p>
|
|
87
|
+
)}
|
|
73
88
|
</div>
|
|
74
89
|
</div>
|
|
75
90
|
</div>
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { useState } from 'preact/hooks';
|
|
2
|
+
import type { FormEvent } from 'preact/compat';
|
|
3
|
+
import { Check, Globe, Hash, Layers, Plus, Sparkles } from 'lucide-preact';
|
|
4
|
+
import type { ProviderItem } from '@/shared/types';
|
|
5
|
+
import { addProviderModel, type ManualModelRequest } from '@/shared/lib/models/provider/models';
|
|
6
|
+
import {
|
|
7
|
+
PROVIDER_THINKING_EFFORTS,
|
|
8
|
+
type ProviderThinkingEffort,
|
|
9
|
+
} from '@/shared/lib/models/provider/dialect';
|
|
10
|
+
import { Modal } from '@/client/components/common/Modal';
|
|
11
|
+
import { EffortLadder } from '@/client/components/settings/categories/provider-settings/EffortLadder';
|
|
12
|
+
import { PriceFields } from '@/client/components/settings/categories/provider-settings/PriceFields';
|
|
13
|
+
|
|
14
|
+
interface AddModelModalProps {
|
|
15
|
+
isOpen: boolean;
|
|
16
|
+
provider: ProviderItem;
|
|
17
|
+
onClose: () => void;
|
|
18
|
+
/** Called after a successful write so the list and the chat picker refresh. */
|
|
19
|
+
onAdded: (modelId: string) => void;
|
|
20
|
+
/** Reports a refusal the dialog cannot fix itself (bad endpoint, bad key). */
|
|
21
|
+
onError: (message: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Register ONE model by hand.
|
|
26
|
+
*
|
|
27
|
+
* The auto-fetch needs a listing endpoint, and plenty of real providers have
|
|
28
|
+
* none worth using: an Azure deployment is a name the user chose, a gateway may
|
|
29
|
+
* serve a model its `/models` route omits, and Bedrock/Vertex expose no
|
|
30
|
+
* `/models` at all. This dialog writes the same models.yml entry the fetch
|
|
31
|
+
* would, with the metadata the user actually knows.
|
|
32
|
+
*
|
|
33
|
+
* Only `id` is required. Every other field is metadata omp can work without —
|
|
34
|
+
* a missing context window is estimated, a missing price is simply not
|
|
35
|
+
* accounted — so requiring them would block the common case of "just add this
|
|
36
|
+
* id I know exists".
|
|
37
|
+
*/
|
|
38
|
+
export function AddModelModal({
|
|
39
|
+
isOpen,
|
|
40
|
+
provider,
|
|
41
|
+
onClose,
|
|
42
|
+
onAdded,
|
|
43
|
+
onError,
|
|
44
|
+
}: AddModelModalProps) {
|
|
45
|
+
const [id, setId] = useState('');
|
|
46
|
+
const [name, setName] = useState('');
|
|
47
|
+
const [contextWindow, setContextWindow] = useState('');
|
|
48
|
+
const [maxTokens, setMaxTokens] = useState('');
|
|
49
|
+
const [costInput, setCostInput] = useState('');
|
|
50
|
+
const [costOutput, setCostOutput] = useState('');
|
|
51
|
+
const [costCacheRead, setCostCacheRead] = useState('');
|
|
52
|
+
const [costCacheWrite, setCostCacheWrite] = useState('');
|
|
53
|
+
const [reasoning, setReasoning] = useState(false);
|
|
54
|
+
const [imageInput, setImageInput] = useState(false);
|
|
55
|
+
const [efforts, setEfforts] = useState<ProviderThinkingEffort[]>([]);
|
|
56
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
57
|
+
|
|
58
|
+
if (!isOpen) return null;
|
|
59
|
+
|
|
60
|
+
const trimmedId = id.trim();
|
|
61
|
+
const duplicate = provider.models.some((model) => model.id === trimmedId);
|
|
62
|
+
const idError = !trimmedId
|
|
63
|
+
? 'Enter the model id exactly as the provider expects it.'
|
|
64
|
+
: duplicate
|
|
65
|
+
? `"${trimmedId}" is already registered under this provider.`
|
|
66
|
+
: '';
|
|
67
|
+
const numberError = (value: string, label: string): string =>
|
|
68
|
+
value.trim() && !(Number(value) > 0) ? `${label} must be a positive number of tokens.` : '';
|
|
69
|
+
const limitsError = numberError(contextWindow, 'Context window') || numberError(maxTokens, 'Max output');
|
|
70
|
+
const costError = (value: string): string =>
|
|
71
|
+
value.trim() && !(Number(value) >= 0) ? 'Prices must be zero or more USD per 1M tokens.' : '';
|
|
72
|
+
const priceError = costError(costInput) || costError(costOutput)
|
|
73
|
+
|| costError(costCacheRead) || costError(costCacheWrite);
|
|
74
|
+
// A price is written only when BOTH headline sides are known: omp's schema
|
|
75
|
+
// requires all four cost fields together, so half a price is not a price. The
|
|
76
|
+
// two cache rates may be left empty — they are written as 0, which is what
|
|
77
|
+
// "no cached-token discount" means.
|
|
78
|
+
const halfPrice = Boolean(costInput.trim()) !== Boolean(costOutput.trim())
|
|
79
|
+
? 'Enter both input and output price, or neither.'
|
|
80
|
+
: (costCacheRead.trim() || costCacheWrite.trim()) && !costInput.trim()
|
|
81
|
+
? 'A cache rate needs the input and output price too.'
|
|
82
|
+
: '';
|
|
83
|
+
// The ladder is a capability of a reasoning model; ticking levels while
|
|
84
|
+
// Reasoning is off would describe a model omp is not told can reason.
|
|
85
|
+
const ladderError = !reasoning && efforts.length > 0
|
|
86
|
+
? 'Turn on Reasoning to declare its levels.'
|
|
87
|
+
: '';
|
|
88
|
+
const canSubmit = Boolean(trimmedId) && !idError && !limitsError && !priceError
|
|
89
|
+
&& !halfPrice && !ladderError && !isSubmitting;
|
|
90
|
+
|
|
91
|
+
const numberOrUndefined = (value: string): number | undefined => {
|
|
92
|
+
const parsed = Number(value);
|
|
93
|
+
return value.trim() && Number.isFinite(parsed) ? parsed : undefined;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const toggleEffort = (effort: ProviderThinkingEffort) => {
|
|
97
|
+
setEfforts((prev) => (
|
|
98
|
+
prev.includes(effort)
|
|
99
|
+
? prev.filter((entry) => entry !== effort)
|
|
100
|
+
// Kept in omp's own order so the written ladder reads lowest-first
|
|
101
|
+
// regardless of the order the user ticked the boxes.
|
|
102
|
+
: PROVIDER_THINKING_EFFORTS.filter((level) => level === effort || prev.includes(level))
|
|
103
|
+
));
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const handleSubmit = async (e: FormEvent) => {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
if (!canSubmit) return;
|
|
109
|
+
setIsSubmitting(true);
|
|
110
|
+
try {
|
|
111
|
+
const request: ManualModelRequest = {
|
|
112
|
+
provider: provider.slug,
|
|
113
|
+
id: trimmedId,
|
|
114
|
+
...(name.trim() ? { name: name.trim() } : {}),
|
|
115
|
+
...(numberOrUndefined(contextWindow) !== undefined
|
|
116
|
+
? { contextWindow: numberOrUndefined(contextWindow) }
|
|
117
|
+
: {}),
|
|
118
|
+
...(numberOrUndefined(maxTokens) !== undefined ? { maxTokens: numberOrUndefined(maxTokens) } : {}),
|
|
119
|
+
...(reasoning ? { reasoning: true } : {}),
|
|
120
|
+
...(reasoning && efforts.length > 0 ? { efforts } : {}),
|
|
121
|
+
...(imageInput ? { imageInput: true } : {}),
|
|
122
|
+
...(costInput.trim() && costOutput.trim()
|
|
123
|
+
? {
|
|
124
|
+
costInput: numberOrUndefined(costInput),
|
|
125
|
+
costOutput: numberOrUndefined(costOutput),
|
|
126
|
+
// Empty cache rates become 0 in the seed builder; sending them only
|
|
127
|
+
// when typed keeps the request honest about what the user entered.
|
|
128
|
+
...(numberOrUndefined(costCacheRead) !== undefined
|
|
129
|
+
? { costCacheRead: numberOrUndefined(costCacheRead) }
|
|
130
|
+
: {}),
|
|
131
|
+
...(numberOrUndefined(costCacheWrite) !== undefined
|
|
132
|
+
? { costCacheWrite: numberOrUndefined(costCacheWrite) }
|
|
133
|
+
: {}),
|
|
134
|
+
}
|
|
135
|
+
: {}),
|
|
136
|
+
// The endpoint and credential are only consulted for a provider with no
|
|
137
|
+
// models.yml entry yet; the writer is add-only and keeps an existing
|
|
138
|
+
// one. A keyless provider passes `auth: none` so omp accepts the entry
|
|
139
|
+
// without a credential.
|
|
140
|
+
...(provider.baseUrl ? { baseUrl: provider.baseUrl } : {}),
|
|
141
|
+
...(provider.apiKey ? { apiKey: provider.apiKey } : {}),
|
|
142
|
+
...(provider.auth ? { auth: provider.auth } : {}),
|
|
143
|
+
};
|
|
144
|
+
const result = await addProviderModel(request);
|
|
145
|
+
if (!result.success) {
|
|
146
|
+
// The refusal is the actionable half (an endpoint override with no
|
|
147
|
+
// credential, a provider not in models.yml) — surface it verbatim.
|
|
148
|
+
onError(result.error || result.reason || `Could not register ${trimmedId}.`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
onAdded(trimmedId);
|
|
152
|
+
onClose();
|
|
153
|
+
} finally {
|
|
154
|
+
setIsSubmitting(false);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<Modal
|
|
160
|
+
onClose={onClose}
|
|
161
|
+
header={
|
|
162
|
+
<div className="flex items-center gap-2">
|
|
163
|
+
<Plus size={16} className="text-ink/70" />
|
|
164
|
+
<div>
|
|
165
|
+
<h3 className="text-sm font-semibold text-ink">Add Model</h3>
|
|
166
|
+
<p className="text-[11px] text-ink/50 font-mono truncate max-w-[280px]">{provider.slug}</p>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
}
|
|
170
|
+
form={{ onSubmit: handleSubmit, className: 'p-5 space-y-4' }}
|
|
171
|
+
footer={
|
|
172
|
+
<div className="pt-2 flex items-center justify-end gap-2 border-t border-ink/10">
|
|
173
|
+
<button
|
|
174
|
+
type="button"
|
|
175
|
+
onClick={onClose}
|
|
176
|
+
className="px-3.5 py-1.5 rounded-md border border-ink/20 text-xs font-medium text-ink hover:bg-ink/5 transition-colors cursor-pointer"
|
|
177
|
+
>
|
|
178
|
+
Cancel
|
|
179
|
+
</button>
|
|
180
|
+
<button
|
|
181
|
+
type="submit"
|
|
182
|
+
disabled={!canSubmit}
|
|
183
|
+
className="px-3.5 py-1.5 rounded-md bg-ink text-canvas text-xs font-medium hover:opacity-90 transition-opacity cursor-pointer disabled:opacity-50 flex items-center gap-1.5"
|
|
184
|
+
>
|
|
185
|
+
{isSubmitting ? (
|
|
186
|
+
<>
|
|
187
|
+
<Layers size={14} className="animate-pulse" />
|
|
188
|
+
<span>Registering...</span>
|
|
189
|
+
</>
|
|
190
|
+
) : (
|
|
191
|
+
<>
|
|
192
|
+
<Check size={14} />
|
|
193
|
+
<span>Add Model</span>
|
|
194
|
+
</>
|
|
195
|
+
)}
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
}
|
|
199
|
+
>
|
|
200
|
+
{/* Model id */}
|
|
201
|
+
<div>
|
|
202
|
+
<label className="block text-xs font-semibold text-ink mb-1">Model ID</label>
|
|
203
|
+
<div className="relative flex items-center">
|
|
204
|
+
<Hash size={13} className="absolute left-2.5 text-ink/40 pointer-events-none" />
|
|
205
|
+
<input
|
|
206
|
+
type="text"
|
|
207
|
+
required
|
|
208
|
+
autoFocus
|
|
209
|
+
value={id}
|
|
210
|
+
onChange={(e) => setId(e.currentTarget.value)}
|
|
211
|
+
placeholder="e.g. claude-sonnet-5 or my-deployment"
|
|
212
|
+
className="w-full bg-paper border border-ink/20 rounded-md pl-8 pr-3 py-1.5 text-xs text-ink outline-none focus:border-ink/60 font-mono"
|
|
213
|
+
/>
|
|
214
|
+
</div>
|
|
215
|
+
{idError && <p className="text-[11px] text-error mt-1">{idError}</p>}
|
|
216
|
+
<p className="text-[10px] text-ink/40 mt-1">
|
|
217
|
+
Sent to the provider verbatim — this is the id omp puts on the wire.
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
{/* Display name */}
|
|
222
|
+
<div>
|
|
223
|
+
<label className="block text-xs font-semibold text-ink mb-1">
|
|
224
|
+
Display Name <span className="font-normal text-ink/40">— optional</span>
|
|
225
|
+
</label>
|
|
226
|
+
<input
|
|
227
|
+
type="text"
|
|
228
|
+
value={name}
|
|
229
|
+
onChange={(e) => setName(e.currentTarget.value)}
|
|
230
|
+
placeholder="Shown in the chat model picker"
|
|
231
|
+
className="w-full bg-paper border border-ink/20 rounded-md px-3 py-1.5 text-xs text-ink outline-none focus:border-ink/60"
|
|
232
|
+
/>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
{/* Limits */}
|
|
236
|
+
<div className="grid grid-cols-2 gap-3">
|
|
237
|
+
<div>
|
|
238
|
+
<label className="block text-xs font-semibold text-ink mb-1">
|
|
239
|
+
Context window <span className="font-normal text-ink/40">— tokens</span>
|
|
240
|
+
</label>
|
|
241
|
+
<input
|
|
242
|
+
type="text"
|
|
243
|
+
inputMode="numeric"
|
|
244
|
+
value={contextWindow}
|
|
245
|
+
onChange={(e) => setContextWindow(e.currentTarget.value)}
|
|
246
|
+
placeholder="200000"
|
|
247
|
+
className="w-full bg-paper border border-ink/20 rounded-md px-3 py-1.5 text-xs text-ink outline-none focus:border-ink/60 font-mono"
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
<div>
|
|
251
|
+
<label className="block text-xs font-semibold text-ink mb-1">
|
|
252
|
+
Max output <span className="font-normal text-ink/40">— tokens</span>
|
|
253
|
+
</label>
|
|
254
|
+
<input
|
|
255
|
+
type="text"
|
|
256
|
+
inputMode="numeric"
|
|
257
|
+
value={maxTokens}
|
|
258
|
+
onChange={(e) => setMaxTokens(e.currentTarget.value)}
|
|
259
|
+
placeholder="8192"
|
|
260
|
+
className="w-full bg-paper border border-ink/20 rounded-md px-3 py-1.5 text-xs text-ink outline-none focus:border-ink/60 font-mono"
|
|
261
|
+
/>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
{limitsError && <p className="text-[11px] text-error -mt-2">{limitsError}</p>}
|
|
265
|
+
|
|
266
|
+
{/* Capabilities */}
|
|
267
|
+
<div>
|
|
268
|
+
<label className="block text-xs font-semibold text-ink mb-1.5">Capabilities</label>
|
|
269
|
+
<div className="grid grid-cols-2 gap-2">
|
|
270
|
+
<button
|
|
271
|
+
type="button"
|
|
272
|
+
onClick={() => setReasoning((prev) => !prev)}
|
|
273
|
+
aria-pressed={reasoning}
|
|
274
|
+
className={`flex items-center gap-2 px-2.5 py-2 rounded-lg border text-left text-[11px] transition-colors cursor-pointer ${
|
|
275
|
+
reasoning ? 'border-ink/50 bg-ink/5 font-semibold text-ink' : 'border-ink/15 hover:border-ink/30 text-ink/70'
|
|
276
|
+
}`}
|
|
277
|
+
>
|
|
278
|
+
<Sparkles size={13} className="shrink-0" />
|
|
279
|
+
<span className="truncate">Reasoning</span>
|
|
280
|
+
</button>
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
onClick={() => setImageInput((prev) => !prev)}
|
|
284
|
+
aria-pressed={imageInput}
|
|
285
|
+
className={`flex items-center gap-2 px-2.5 py-2 rounded-lg border text-left text-[11px] transition-colors cursor-pointer ${
|
|
286
|
+
imageInput ? 'border-ink/50 bg-ink/5 font-semibold text-ink' : 'border-ink/15 hover:border-ink/30 text-ink/70'
|
|
287
|
+
}`}
|
|
288
|
+
>
|
|
289
|
+
<Globe size={13} className="shrink-0" />
|
|
290
|
+
<span className="truncate">Vision</span>
|
|
291
|
+
</button>
|
|
292
|
+
</div>
|
|
293
|
+
<p className="text-[10px] text-ink/40 mt-1">
|
|
294
|
+
Leave both off when unsure — omp still serves the model; only its declared abilities change.
|
|
295
|
+
</p>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
{/* Reasoning levels — only meaningful for a reasoning model */}
|
|
299
|
+
{reasoning && (
|
|
300
|
+
<EffortLadder
|
|
301
|
+
selected={efforts}
|
|
302
|
+
onToggle={toggleEffort}
|
|
303
|
+
onSelectAll={() => setEfforts([...PROVIDER_THINKING_EFFORTS])}
|
|
304
|
+
onClear={() => setEfforts([])}
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
307
|
+
{ladderError && <p className="text-[11px] text-error -mt-2">{ladderError}</p>}
|
|
308
|
+
|
|
309
|
+
<PriceFields
|
|
310
|
+
costInput={costInput}
|
|
311
|
+
costOutput={costOutput}
|
|
312
|
+
costCacheRead={costCacheRead}
|
|
313
|
+
costCacheWrite={costCacheWrite}
|
|
314
|
+
onCostInputChange={setCostInput}
|
|
315
|
+
onCostOutputChange={setCostOutput}
|
|
316
|
+
onCostCacheReadChange={setCostCacheRead}
|
|
317
|
+
onCostCacheWriteChange={setCostCacheWrite}
|
|
318
|
+
error={priceError || halfPrice}
|
|
319
|
+
/>
|
|
320
|
+
</Modal>
|
|
321
|
+
);
|
|
322
|
+
}
|