@k2b/cloud 0.43.0 → 0.45.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/package.json +11 -2
- package/src/_internal/app-access-request.ts +13 -0
- package/src/_internal/app-access-resources.ts +141 -0
- package/src/_internal/app-event-catalog.ts +40 -0
- package/src/_internal/app-event-schemas.ts +127 -0
- package/src/_internal/application-package.ts +40 -0
- package/src/_internal/built-in-app-ids.json +27 -0
- package/src/_internal/capabilities.ts +158 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +7 -5
- package/src/_internal/define-app.ts +57 -6
- package/src/_internal/object-store-area.ts +9 -0
- package/src/_internal/object-store-config.ts +101 -0
- package/src/_internal/object-store-key.ts +14 -0
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +11 -1
- package/src/_internal/registry.ts +8 -1
- package/src/_internal/runtime-context.ts +8 -1
- package/src/access/AppAccessEditor.tsx +2 -0
- package/src/access/GroupCoverage.tsx +20 -5
- package/src/access/PermissionEditor.tsx +20 -5
- package/src/access/PrincipalAvatar.tsx +30 -21
- package/src/access/PrincipalPicker.tsx +14 -4
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-messages.ts +6 -0
- package/src/access/messages.ts +4 -4
- package/src/account/EntitySearch.tsx +12 -0
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +7 -0
- package/src/ai/admin.ts +3 -0
- package/src/ai/app-frame.ts +12 -0
- package/src/ai/application-tasks.ts +101 -0
- package/src/ai/assistant-models.ts +126 -64
- package/src/ai/capabilities.ts +19 -4
- package/src/ai/chart-block.ts +63 -30
- package/src/ai/chat/chart-block.tsx +12 -3
- package/src/ai/chat/composer-adapter.ts +8 -7
- package/src/ai/chat/message-actions.tsx +1 -1
- package/src/ai/chat/message-utils.ts +2 -2
- package/src/ai/chat/presentation.tsx +22 -1
- package/src/ai/chat-quotas.ts +5 -4
- package/src/ai/chat-tasks.ts +2 -2
- package/src/ai/client/controller.ts +11 -10
- package/src/ai/client/projection.ts +3 -3
- package/src/ai/compaction.ts +3 -3
- package/src/ai/credentials.ts +13 -114
- package/src/ai/default-tool-contracts.ts +4 -3
- package/src/ai/dictation-runtime.ts +10 -6
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +40 -25
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +12 -5
- package/src/ai/inference-calls.ts +5 -4
- package/src/ai/maintenance.ts +1 -1
- package/src/ai/memory-learning-runs.ts +5 -5
- package/src/ai/memory-learning.ts +3 -3
- package/src/ai/message-queue.ts +1 -2
- package/src/ai/migrate.ts +75 -42
- package/src/ai/model-access-migrate.ts +3 -17
- package/src/ai/model-access.ts +44 -85
- package/src/ai/model-card.tsx +134 -0
- package/src/ai/model-catalog.ts +359 -0
- package/src/ai/model-chooser-messages.ts +88 -0
- package/src/ai/model-chooser.tsx +577 -0
- package/src/ai/model-delete.ts +234 -0
- package/src/ai/model-fixture.ts +39 -0
- package/src/ai/model-icon.tsx +36 -0
- package/src/ai/model-overview.ts +53 -0
- package/src/ai/model-usage.ts +45 -0
- package/src/ai/openai-api-messages.ts +0 -4
- package/src/ai/openai-api.ts +26 -9
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/project-tool.ts +4 -4
- package/src/ai/projects-authorization.ts +53 -0
- package/src/ai/projects-routes.ts +58 -47
- package/src/ai/projects.ts +110 -88
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +55 -6
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider.ts +1 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +4 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +9 -3
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +47 -48
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- package/src/ai/skill-tool.ts +4 -4
- package/src/ai/skills-routes.ts +27 -26
- package/src/ai/skills.ts +120 -81
- package/src/ai/store.ts +45 -23
- package/src/ai/stream.ts +1 -1
- package/src/ai/structured-runs.ts +2 -2
- package/src/ai/structured.ts +21 -44
- package/src/ai/task-contracts.ts +25 -5
- package/src/ai/task-execution.ts +15 -6
- package/src/ai/task-model.ts +9 -0
- package/src/ai/transcription.ts +27 -22
- package/src/ai/types.ts +50 -62
- package/src/ai/ui.tsx +13 -0
- package/src/ai/usage.ts +8 -8
- package/src/ai/validate.ts +8 -7
- package/src/ai/view-block.ts +2 -2
- package/src/api/accounts-entities.ts +22 -3
- package/src/api/admin-ai-quotas.ts +34 -56
- package/src/api/admin-ai-skills.ts +4 -1
- package/src/api/admin-ai-usage.ts +1 -1
- package/src/api/admin-ai.ts +173 -0
- package/src/api/admin-core-settings.ts +68 -244
- package/src/api/app-approval.ts +3 -1
- package/src/api/app-event-delivery.ts +51 -0
- package/src/api/app-events.ts +123 -0
- package/src/api/app-resource-access.ts +48 -0
- package/src/api/auth/account-mismatch.ts +24 -0
- package/src/api/auth.ts +35 -5
- package/src/api/capabilities.ts +28 -4
- package/src/api/index.ts +7 -1
- package/src/api/mcp.ts +175 -2
- package/src/api/me.ts +6 -4
- package/src/api/pwa-phone.ts +75 -26
- package/src/api/pwa.ts +34 -8
- package/src/api/resource-links.ts +83 -6
- package/src/api/resource-resolve.ts +50 -0
- package/src/api/search/schemas.ts +3 -1
- package/src/api/search.ts +9 -7
- package/src/app-pwa/contracts.ts +4 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +1 -0
- package/src/app-pwa/service.ts +91 -0
- package/src/app-pwa/transport.ts +22 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/session.ts +39 -0
- package/src/browser/shared-text-history-validation.ts +151 -0
- package/src/browser/shared-text-history.ts +464 -0
- package/src/browser/shared-text-offline-authorized.ts +29 -0
- package/src/browser/shared-text-offline.ts +351 -0
- package/src/browser/shared-text-provider.ts +714 -0
- package/src/browser/shared-text-table-contract.ts +150 -0
- package/src/browser/shared-text.ts +19 -0
- package/src/capabilities/server-invoke.ts +2 -11
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/admin/ai-models.ts +199 -0
- package/src/cli/admin/ai-quotas.ts +6 -102
- package/src/cli/admin/ai-usage.ts +4 -4
- package/src/cli/admin/app-credentials.ts +5 -3
- package/src/cli/admin/index.ts +5 -2
- package/src/cli/apps.ts +89 -26
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +26 -2
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +21 -5
- package/src/contracts/capabilities.ts +143 -3
- package/src/contracts/index.ts +2 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/pwa.ts +17 -1
- package/src/contracts/registry.ts +6 -0
- package/src/contracts/resource-clipboard.ts +35 -0
- package/src/contracts/resource-pages.ts +140 -0
- package/src/contracts/resource-resolution.ts +13 -0
- package/src/contracts/shared.ts +6 -1
- package/src/contracts/user-notifications.ts +2 -0
- package/src/events/app-event-delivery.ts +100 -0
- package/src/events/app-events.testing.ts +65 -0
- package/src/events/app-events.ts +134 -0
- package/src/events/core-user-created.ts +24 -0
- package/src/events/index.ts +9 -0
- package/src/events/testing.ts +2 -0
- package/src/index.ts +1 -1
- package/src/server/api/respond.ts +17 -4
- package/src/server/index.ts +2 -0
- package/src/server/middleware/app-access.ts +1 -1
- package/src/server/middleware/auth.ts +21 -7
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +2 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +31 -10
- package/src/server/services/index.ts +1 -0
- package/src/services/accounts/app.ts +36 -13
- package/src/services/accounts/entities.ts +43 -19
- package/src/services/accounts/group-sql.ts +23 -0
- package/src/services/accounts/groups.ts +19 -14
- package/src/services/accounts/identities.ts +70 -11
- package/src/services/accounts/identity-reconciliation.ts +3 -1
- package/src/services/accounts/local-groups.ts +69 -4
- package/src/services/accounts/users.ts +45 -14
- package/src/services/app-access.ts +25 -7
- package/src/services/app-details.ts +224 -0
- package/src/services/app-discovery.ts +2 -1
- package/src/services/app-event-shapes.ts +139 -0
- package/src/services/app-events/accept.ts +116 -0
- package/src/services/app-events/admin.ts +250 -0
- package/src/services/app-events/declarations.ts +187 -0
- package/src/services/app-events/deliver.ts +218 -0
- package/src/services/app-events.ts +92 -0
- package/src/services/config-conflict.ts +11 -0
- package/src/services/core-user-events.ts +1 -0
- package/src/services/freeipa-config.ts +9 -0
- package/src/services/geo/ledger.ts +28 -10
- package/src/services/geo/service.ts +43 -24
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/invocation-actor.ts +20 -2
- package/src/services/identity/invocation-authority.ts +4 -0
- package/src/services/identity/invocation-token.ts +238 -56
- package/src/services/identity/issue-invocation.ts +6 -1
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +13 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/index.ts +2 -2
- package/src/services/mandates/policy.ts +20 -12
- package/src/services/notifications/browser.ts +4 -0
- package/src/services/notifications/catalog.ts +5 -2
- package/src/services/notifications/platform.ts +7 -2
- package/src/services/notifications/user.ts +18 -7
- package/src/services/oauth-tokens.ts +28 -2
- package/src/services/object-store.ts +217 -0
- package/src/services/providers/local/users.ts +5 -0
- package/src/services/pwa-devices.ts +147 -37
- package/src/services/session/index.ts +26 -3
- package/src/services/settings/core-settings.ts +71 -43
- package/src/services/settings/index.ts +8 -0
- package/src/services/settings/versions.ts +26 -0
- package/src/shared/ai-models.ts +274 -0
- package/src/shared/ai-quotas.ts +2 -2
- package/src/shared/ai-usage.ts +2 -2
- package/src/shared/email-html.ts +1 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/markdown/extensions/katex.ts +8 -1
- package/src/shared/markdown/extensions/tables.ts +14 -1
- package/src/shared/markdown/index.ts +22 -11
- package/src/shared-text/anchors.ts +29 -0
- package/src/shared-text/awareness.ts +87 -0
- package/src/shared-text/changes.ts +27 -0
- package/src/shared-text/documents.ts +433 -65
- package/src/shared-text/events.ts +12 -2
- package/src/shared-text/index.ts +16 -1
- package/src/shared-text/log.ts +24 -5
- package/src/shared-text/snapshots.ts +69 -23
- package/src/shared-text/structural-history.ts +377 -0
- package/src/shared-text/table-actions.ts +432 -0
- package/src/shared-text/text-diff.ts +75 -0
- package/src/shared-text/types.ts +24 -5
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +9 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +2 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +2 -2
- package/src/ssr/CloudShell.island.tsx +44 -7
- package/src/ssr/Layout.tsx +9 -7
- package/src/ssr/MobileProfileActions.tsx +2 -2
- package/src/ssr/ProfilePreferences.tsx +2 -1
- package/src/ssr/PwaLayout.tsx +16 -1
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -31
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-launchpad.tsx +6 -1
- package/src/ssr/index.ts +8 -0
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +11 -1
- package/src/ssr/profile-actions.ts +3 -6
- package/src/ssr/pwa-messages.ts +2 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +3 -1
- package/src/styles/global.css +1 -0
- package/src/styles/profile-picture.css +270 -0
- package/src/styles/utilities-navigation.css +6 -0
- package/src/workflows/ai/policy.ts +49 -0
- package/src/workflows/ai/runtime.ts +131 -21
- package/src/workflows/ai/store.ts +283 -35
- package/src/workflows/ai/types.ts +8 -2
- package/src/workflows/ai-actions.ts +148 -120
- package/src/workflows/authoring.ts +31 -0
- package/src/workflows/contracts.ts +1 -0
- package/src/workflows/definition.ts +2 -0
- package/src/workflows/editor.ts +15 -0
- package/src/workflows/index.ts +2 -1
- package/src/workflows/language/compiler.ts +34 -3
- package/src/workflows/language/expressions.ts +3 -2
- package/src/workflows/runtime/executor.ts +134 -34
- package/src/workflows/runtime/index.ts +2 -0
- package/src/workflows/runtime/ports.ts +5 -1
- package/src/workflows/store/actions.ts +1 -1
- package/src/workflows/store/definitions.ts +5 -0
- package/src/workflows/store/runs.ts +5 -3
- package/src/workflows/store/worker.ts +7 -2
- package/src/ai/model-pricing.ts +0 -43
- package/src/ai/model-request-settings.ts +0 -98
- package/src/ai/settings-messages.ts +0 -129
- package/src/shared/ai-model-request-settings.ts +0 -21
package/src/ai/capabilities.ts
CHANGED
|
@@ -295,7 +295,8 @@ export const searchAiTools = (
|
|
|
295
295
|
.flatMap((entry) => {
|
|
296
296
|
const name = normalizeSearchText(entry.name);
|
|
297
297
|
const title = normalizeSearchText(entry.title);
|
|
298
|
-
const
|
|
298
|
+
const localizedTitle = normalizeSearchText(entry.capability?.display.title ?? "");
|
|
299
|
+
const identity = `${name} ${title} ${localizedTitle}`;
|
|
299
300
|
const app = normalizeSearchText(`${entry.appId ?? ""} ${entry.searchText}`);
|
|
300
301
|
const description = normalizeSearchText(entry.description);
|
|
301
302
|
const identityWords = searchWordForms(identity);
|
|
@@ -303,7 +304,7 @@ export const searchAiTools = (
|
|
|
303
304
|
const descriptionWords = searchWordForms(description);
|
|
304
305
|
let matchedTerms = 0;
|
|
305
306
|
let score = 0;
|
|
306
|
-
if (name === phrase || title === phrase) score += 100;
|
|
307
|
+
if (name === phrase || title === phrase || localizedTitle === phrase) score += 100;
|
|
307
308
|
else if (includesSearchPhrase(identity, phrase)) score += 50;
|
|
308
309
|
if (!input.appId && app === phrase) score += 40;
|
|
309
310
|
else if (!input.appId && includesSearchPhrase(app, phrase)) score += 20;
|
|
@@ -324,6 +325,15 @@ export const searchAiTools = (
|
|
|
324
325
|
right.matchedTerms - left.matchedTerms || right.score - left.score || left.entry.name.localeCompare(right.entry.name),
|
|
325
326
|
)
|
|
326
327
|
.slice(0, DEFAULT_SEARCH_LIMIT);
|
|
328
|
+
// appId is a discovery hint, not the conversation's authorization scope. The
|
|
329
|
+
// resolver has already removed inaccessible, unoffered and disallowed tools.
|
|
330
|
+
// Keep successful scoped searches precise; recover a mistaken owner once.
|
|
331
|
+
if (matches.length === 0 && input.appId) {
|
|
332
|
+
return searchAiTools(
|
|
333
|
+
catalog.filter((entry) => entry.appId !== input.appId),
|
|
334
|
+
{ query: input.query },
|
|
335
|
+
);
|
|
336
|
+
}
|
|
327
337
|
return {
|
|
328
338
|
tools: matches.map(({ entry }) => ({
|
|
329
339
|
name: entry.name,
|
|
@@ -669,11 +679,16 @@ export const createAiToolMetaTools = (input: {
|
|
|
669
679
|
: "";
|
|
670
680
|
const search = defineAiTool({
|
|
671
681
|
name: "search_tools",
|
|
672
|
-
description: `Search available Cloud tools and installed app operations by concise task terms.${liveAppDirectory} When the app is known, set its exact appId
|
|
682
|
+
description: `Search available Cloud tools and installed app operations by concise task terms.${liveAppDirectory} When the app is known, set its exact appId. If that app has no matches, search also returns matching available tools from other apps or built-ins. If the owner is unclear, omit appId or use list_apps. This only discovers tools; call load_tools with the exact returned names before using deferred tools.${unavailableLoadedNotice}`,
|
|
673
683
|
inputSchema: z
|
|
674
684
|
.object({
|
|
675
685
|
query: z.string().trim().min(1).max(200).describe("What the tool should do."),
|
|
676
|
-
appId: z
|
|
686
|
+
appId: z
|
|
687
|
+
.string()
|
|
688
|
+
.trim()
|
|
689
|
+
.min(1)
|
|
690
|
+
.optional()
|
|
691
|
+
.describe("Preferred exact Cloud app id; an empty scoped search falls back to other available tools."),
|
|
677
692
|
})
|
|
678
693
|
.strict(),
|
|
679
694
|
outputSchema: z.object({ tools: z.array(ToolCatalogItemSchema).max(DEFAULT_SEARCH_LIMIT) }).strict(),
|
package/src/ai/chart-block.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Named renderers, not the `charts` namespace, so bundles leave out the map and state timeline renderers.
|
|
2
2
|
import {
|
|
3
3
|
autoBin,
|
|
4
|
-
bar,
|
|
5
4
|
barGauge,
|
|
6
5
|
boxplot,
|
|
7
6
|
type ChartDatum,
|
|
@@ -21,11 +20,14 @@ import {
|
|
|
21
20
|
sparkline,
|
|
22
21
|
stat,
|
|
23
22
|
} from "@k2b/stdlib";
|
|
23
|
+
import { type BarChartOptions, barChartLayout, renderBarChartSvg } from "@k2b/ui/charts";
|
|
24
24
|
import { z } from "zod";
|
|
25
25
|
import type { CloudAiChartInput, CloudAiFormat, CloudAiNumberFormat } from "./default-tool-contracts";
|
|
26
26
|
|
|
27
27
|
type RenderOptions = {
|
|
28
|
-
[K in CloudAiChartInput["kind"]]: { kind: K } &
|
|
28
|
+
[K in CloudAiChartInput["kind"]]: { kind: K } & (K extends "bar"
|
|
29
|
+
? BarChartOptions
|
|
30
|
+
: Omit<Parameters<(typeof charts)[K]>[0], "width" | "height" | "inspect">);
|
|
29
31
|
}[CloudAiChartInput["kind"]];
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -205,18 +207,26 @@ type Formats = {
|
|
|
205
207
|
x: { tick: (value: number) => string; value: (value: number) => string };
|
|
206
208
|
};
|
|
207
209
|
|
|
208
|
-
/**
|
|
209
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Value precision and tick precision share the same style and units; ticks must remain distinct. A currency without
|
|
212
|
+
* `decimals` or compact notation writes cents only when one of the formatted `values` has them: 418,000 € rather than
|
|
213
|
+
* 418,000.00 €.
|
|
214
|
+
*/
|
|
215
|
+
export const cloudAiNumberFormatters = (format: CloudAiNumberFormat | undefined, locale: string, values?: readonly number[]) => {
|
|
210
216
|
const common: Intl.NumberFormatOptions = {
|
|
211
217
|
style: format?.style === "number" ? "decimal" : format?.style,
|
|
212
218
|
currency: format?.currency,
|
|
213
219
|
};
|
|
220
|
+
// Compact notation keeps its fraction: 1.2K and 1.4K must not both read 1K.
|
|
221
|
+
const decimals =
|
|
222
|
+
format?.decimals ??
|
|
223
|
+
(format?.style === "currency" && !format.compact && values?.length && values.every(Number.isInteger) ? 0 : undefined);
|
|
214
224
|
const value = new Intl.NumberFormat(
|
|
215
225
|
locale,
|
|
216
226
|
format
|
|
217
227
|
? {
|
|
218
228
|
...common,
|
|
219
|
-
...(
|
|
229
|
+
...(decimals === undefined ? {} : { minimumFractionDigits: decimals, maximumFractionDigits: decimals }),
|
|
220
230
|
...(format.compact ? { notation: "compact" } : {}),
|
|
221
231
|
}
|
|
222
232
|
: { maximumSignificantDigits: 15 },
|
|
@@ -234,10 +244,37 @@ export const cloudAiNumberFormatters = (format: CloudAiNumberFormat | undefined,
|
|
|
234
244
|
return { value: (v: number) => `${value.format(v)}${unit}`, tick: (v: number) => `${tickValue(v)}${unit}` };
|
|
235
245
|
};
|
|
236
246
|
const numberFormat = (format: CloudAiFormat | undefined) => (format && !("date" in format) ? format : undefined);
|
|
247
|
+
/** The values a chart writes with its value format, for whole-number currency. */
|
|
248
|
+
const formattedValues = (input: CloudAiChartInput): number[] => {
|
|
249
|
+
switch (input.kind) {
|
|
250
|
+
case "line":
|
|
251
|
+
case "scatter":
|
|
252
|
+
return [...input.series.flatMap((series) => series.data.map((point) => point.y)), ...referencesOf(input, "y")];
|
|
253
|
+
case "bar":
|
|
254
|
+
return [...input.data.map((item) => item.value), ...referencesOf(input, "y")];
|
|
255
|
+
case "pie":
|
|
256
|
+
case "donut":
|
|
257
|
+
case "heatmap":
|
|
258
|
+
return input.data.map((item) => item.value);
|
|
259
|
+
case "barGauge":
|
|
260
|
+
return [...input.data.flatMap((item) => [item.value, item.min, item.max]), input.min, input.max].filter(
|
|
261
|
+
(value) => value !== undefined,
|
|
262
|
+
);
|
|
263
|
+
case "stat":
|
|
264
|
+
return [input.value, ...(input.delta === undefined ? [] : [input.delta]), ...(input.sparkline ?? [])];
|
|
265
|
+
case "gauge":
|
|
266
|
+
return [input.value, input.min, input.max].filter((value) => value !== undefined);
|
|
267
|
+
case "sparkline":
|
|
268
|
+
return input.data;
|
|
269
|
+
// Box plots write quartiles they compute, and histograms bin edges: those keep their cents.
|
|
270
|
+
default:
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
};
|
|
237
274
|
const formats = (input: CloudAiChartInput, locale: string): Formats => {
|
|
238
275
|
const yFormat = "yAxis" in input ? numberFormat(input.yAxis?.format) : "format" in input ? input.format : undefined;
|
|
239
|
-
const y = cloudAiNumberFormatters(yFormat, locale);
|
|
240
|
-
const xFormat = "xAxis" in input ? input.xAxis?.format : undefined;
|
|
276
|
+
const y = cloudAiNumberFormatters(yFormat, locale, formattedValues(input));
|
|
277
|
+
const xFormat = "xAxis" in input && input.kind !== "bar" ? input.xAxis?.format : undefined;
|
|
241
278
|
const x = cloudAiNumberFormatters(numberFormat(xFormat), locale);
|
|
242
279
|
// An unformatted x axis often counts years or steps, which compact notation would turn into 2.02K.
|
|
243
280
|
const xTick = new Intl.NumberFormat(locale, { maximumSignificantDigits: 15, useGrouping: "min2" });
|
|
@@ -268,7 +305,7 @@ const leftPadding = (values: number[], value: ChartAxisInput | undefined, tick:
|
|
|
268
305
|
};
|
|
269
306
|
|
|
270
307
|
/**
|
|
271
|
-
*
|
|
308
|
+
* Box plot labels never shorten: when they would overlap in the narrowest column, every n-th label shows in full and
|
|
272
309
|
* the others stay empty, as in `cloud.chart()`. Table and tooltips keep every label.
|
|
273
310
|
*/
|
|
274
311
|
const thinned = <T extends { label: string }>(data: T[], left: number, width = WIDTH, factor = NARROW): T[] => {
|
|
@@ -327,31 +364,27 @@ export function cloudAiChartRenderOptions(
|
|
|
327
364
|
switch (input.kind) {
|
|
328
365
|
case "bar":
|
|
329
366
|
case "boxplot": {
|
|
330
|
-
const values = input.kind === "bar" ? input.data.map((item) => item.value) : input.data.flatMap((item) => item.values);
|
|
331
367
|
const yAxis = referencedAxis(input, "y");
|
|
332
|
-
// The
|
|
333
|
-
|
|
334
|
-
const left = leftPadding(
|
|
335
|
-
[...values, ...referencesOf(input, "y"), ...(input.kind === "bar" && input.yAxis?.scale !== "log" ? [0] : [])],
|
|
336
|
-
yAxis,
|
|
337
|
-
yFormat,
|
|
338
|
-
factor,
|
|
339
|
-
);
|
|
340
|
-
if (input.kind === "boxplot")
|
|
368
|
+
// The bar renderer places every category label and printed value itself, sized for the narrowest column.
|
|
369
|
+
if (input.kind === "bar")
|
|
341
370
|
return {
|
|
342
|
-
kind: "
|
|
343
|
-
|
|
344
|
-
|
|
371
|
+
kind: "bar",
|
|
372
|
+
data: input.data,
|
|
373
|
+
xAxis: input.xAxis,
|
|
374
|
+
yAxis: axis(yAxis, options.formats?.y ?? format.tick),
|
|
375
|
+
// A host's own function format writes printed values like its ticks and table cells.
|
|
376
|
+
valueFormat: options.formats?.value ?? options.formats?.y ?? format.value,
|
|
377
|
+
colorByBar: input.colorByBar,
|
|
378
|
+
showValues: input.showValues,
|
|
379
|
+
legend: options.legend === "outside" ? false : input.legend,
|
|
345
380
|
references,
|
|
346
|
-
|
|
381
|
+
...(options.width === undefined ? {} : { displayWidth: options.width }),
|
|
347
382
|
};
|
|
383
|
+
const left = leftPadding([...input.data.flatMap((item) => item.values), ...referencesOf(input, "y")], yAxis, format.tick, factor);
|
|
348
384
|
return {
|
|
349
|
-
kind: "
|
|
350
|
-
|
|
351
|
-
yAxis: axis(yAxis,
|
|
352
|
-
colorByBar: input.colorByBar,
|
|
353
|
-
showValues: input.showValues,
|
|
354
|
-
legend: options.legend === "outside" ? false : input.legend,
|
|
385
|
+
kind: "boxplot",
|
|
386
|
+
groups: thinned(input.data, left, width, factor),
|
|
387
|
+
yAxis: axis(yAxis, format.tick),
|
|
355
388
|
references,
|
|
356
389
|
padding: { left },
|
|
357
390
|
};
|
|
@@ -478,7 +511,7 @@ const draw = (options: RenderOptions): string => {
|
|
|
478
511
|
const common = { width: WIDTH, height: HEIGHT, inspect: true };
|
|
479
512
|
switch (options.kind) {
|
|
480
513
|
case "bar":
|
|
481
|
-
return
|
|
514
|
+
return renderBarChartSvg({ ...options, ...common, height: barChartLayout(options).height });
|
|
482
515
|
case "line":
|
|
483
516
|
return line({ ...options, ...common });
|
|
484
517
|
case "scatter":
|
|
@@ -554,7 +587,7 @@ export function cloudAiChartRows(
|
|
|
554
587
|
const t = labels.resolve([locale]).t;
|
|
555
588
|
const format = formats(input, locale);
|
|
556
589
|
const valueFormat = input.tooltip?.format
|
|
557
|
-
? cloudAiNumberFormatters(input.tooltip.format, locale).value
|
|
590
|
+
? cloudAiNumberFormatters(input.tooltip.format, locale, formattedValues(input)).value
|
|
558
591
|
: (options.formats?.value ?? (input.kind === "histogram" ? undefined : options.formats?.y) ?? format.value);
|
|
559
592
|
const xFormat = options.formats?.x ?? format.x.value;
|
|
560
593
|
const series = seriesOf(input);
|
|
@@ -50,6 +50,9 @@ const drawChart = (input: CloudAiChartInput, locale: string): DrawnChart | null
|
|
|
50
50
|
* 280-unit drawing; at 12rem a row is still taller than its text.
|
|
51
51
|
*/
|
|
52
52
|
const chartHeight = (kind: Kind) => (kind === "sparkline" ? "8rem" : kind === "stat" ? "10rem" : kind === "barGauge" ? "12rem" : "18rem");
|
|
53
|
+
/** A drawing taller than the usual 280 units, such as many bar rows, grows its frame up to 40rem and scrolls inside. */
|
|
54
|
+
const grownHeight = (kind: Kind, chart: DrawnChart | null) =>
|
|
55
|
+
chart && chart.snapshot.height > 280 ? `min(40rem, max(${chartHeight(kind)}, ${chart.snapshot.height}px))` : chartHeight(kind);
|
|
53
56
|
|
|
54
57
|
function ChartFrame(props: {
|
|
55
58
|
title: string;
|
|
@@ -60,6 +63,11 @@ function ChartFrame(props: {
|
|
|
60
63
|
cursor?: ReturnType<typeof createChartCursor>;
|
|
61
64
|
/** A chart of a titled group: its title sits one heading level and one size below the group's. */
|
|
62
65
|
nested?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The frame takes the drawing's height (`grownHeight`). Only for a drawing present with the first frame: a frame
|
|
68
|
+
* that waits for its data keeps the kind's height, so the data never moves the chat.
|
|
69
|
+
*/
|
|
70
|
+
grow?: boolean;
|
|
63
71
|
}) {
|
|
64
72
|
const locale = useLocale();
|
|
65
73
|
const columns = () =>
|
|
@@ -80,7 +88,7 @@ function ChartFrame(props: {
|
|
|
80
88
|
title={props.title}
|
|
81
89
|
headingLevel={props.nested ? 4 : 3}
|
|
82
90
|
description={props.subtitle ? <span class="text-sm text-secondary">{props.subtitle}</span> : undefined}
|
|
83
|
-
height={chartHeight(props.kind)}
|
|
91
|
+
height={props.grow ? grownHeight(props.kind, props.chart) : chartHeight(props.kind)}
|
|
84
92
|
data={props.chart ? { chart: props.chart.snapshot, rows: props.chart.table.rows } : { chart: emptySnapshot(props.kind), rows: [] }}
|
|
85
93
|
columns={columns()}
|
|
86
94
|
legend={props.chart?.legend.length ? props.chart.legend : undefined}
|
|
@@ -166,7 +174,8 @@ const groupSummary = (group: CloudAiGroupView): GroupSummary => ({
|
|
|
166
174
|
|
|
167
175
|
/**
|
|
168
176
|
* One `chart` tool call: the shared chart with its data table and Copy data, or a group of them. The block takes its
|
|
169
|
-
* full size with its first frame and keeps it
|
|
177
|
+
* full size with its first frame and keeps it, up to 40rem for a tall single chart; switching to the table scrolls
|
|
178
|
+
* inside the same height. A call whose
|
|
170
179
|
* arguments are not a valid chart shows nothing while it runs, because the model gets the validation error and the
|
|
171
180
|
* call fails.
|
|
172
181
|
*/
|
|
@@ -198,7 +207,7 @@ export function CloudChartBlock(props: { args: unknown; completed: boolean }) {
|
|
|
198
207
|
}
|
|
199
208
|
>
|
|
200
209
|
{(value) => (
|
|
201
|
-
<ChartFrame title={value().input.title} subtitle={value().input.subtitle} kind={value().input.kind} chart={value()} />
|
|
210
|
+
<ChartFrame title={value().input.title} subtitle={value().input.subtitle} kind={value().input.kind} chart={value()} grow />
|
|
202
211
|
)}
|
|
203
212
|
</Show>
|
|
204
213
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChatAttachment, ChatMention, ChatModelOption, ChatSubmitInput } from "@k2b/ui";
|
|
2
2
|
import { AI_TURN_IMAGE_MAX_TOTAL_BYTES } from "../limits";
|
|
3
3
|
import type { AiResourceMarker } from "../resource-markers";
|
|
4
|
-
import type { AiDraftContentPart,
|
|
4
|
+
import type { AiDraftContentPart, AiUserContentPart } from "../types";
|
|
5
5
|
import {
|
|
6
6
|
type AiComposerAttachment,
|
|
7
7
|
FILE_INPUT_ACCEPT,
|
|
@@ -45,14 +45,15 @@ export const aiComposerFileAccept = FILE_INPUT_ACCEPT;
|
|
|
45
45
|
*/
|
|
46
46
|
export const aiComposerChooseMaxBytes = (attached: number): number => Math.max(0, MAX_ATTACHMENTS - attached) * VFS_FILE_MAX_BYTES;
|
|
47
47
|
|
|
48
|
-
export const aiChatModelOptions = (
|
|
48
|
+
export const aiChatModelOptions = (
|
|
49
|
+
profiles: readonly import("../../shared/ai-models").AiPeopleModels["options"][number][],
|
|
50
|
+
): ChatModelOption[] =>
|
|
49
51
|
profiles.map((profile) => ({
|
|
50
52
|
id: profile.id,
|
|
51
|
-
label: profile.label,
|
|
52
|
-
description: profile.
|
|
53
|
-
image: profile.image,
|
|
54
|
-
icon:
|
|
55
|
-
capabilities: profile.capabilities,
|
|
53
|
+
label: [profile.label, profile.variantLabel].filter(Boolean).join(" · "),
|
|
54
|
+
description: profile.description,
|
|
55
|
+
image: profile.icon.kind === "image" ? profile.icon.src : undefined,
|
|
56
|
+
icon: "ti ti-sparkles",
|
|
56
57
|
}));
|
|
57
58
|
|
|
58
59
|
const isTextAttachment = (
|
|
@@ -109,7 +109,7 @@ const assistantResponseInfo = (entries: AiStoredMessage[], locale: string) => {
|
|
|
109
109
|
|
|
110
110
|
const meta = [
|
|
111
111
|
{ label: t.infoProviderModel, value: entry.providerModel ?? t.infoUnknown },
|
|
112
|
-
{ label: t.infoModelProfile, value: entry.
|
|
112
|
+
{ label: t.infoModelProfile, value: entry.modelId ?? t.infoUnknown },
|
|
113
113
|
{ label: t.infoLoopId, value: entry.loopId ?? t.infoLegacy },
|
|
114
114
|
{ label: t.infoFinished, value: describeResponseEnd(entry, locale) },
|
|
115
115
|
{ label: t.infoCreated, value: formatDateTime(entry.createdAt, locale) },
|
|
@@ -259,7 +259,7 @@ export const textAttachmentSummariesFromMessage = (message: Message) => {
|
|
|
259
259
|
export type AiLatestUsageSnapshot = {
|
|
260
260
|
request: Usage;
|
|
261
261
|
loop: Usage;
|
|
262
|
-
|
|
262
|
+
modelId: string | null;
|
|
263
263
|
};
|
|
264
264
|
|
|
265
265
|
export const latestUsageSnapshot = (messages: AiStoredMessage[]): AiLatestUsageSnapshot | null => {
|
|
@@ -287,7 +287,7 @@ export const latestUsageSnapshot = (messages: AiStoredMessage[]): AiLatestUsageS
|
|
|
287
287
|
{ input: 0, output: 0, total: 0 },
|
|
288
288
|
)
|
|
289
289
|
: request),
|
|
290
|
-
|
|
290
|
+
modelId: entry?.modelId ?? null,
|
|
291
291
|
};
|
|
292
292
|
}
|
|
293
293
|
// A provider without usage must not inherit a previous request's numbers.
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
Show,
|
|
14
14
|
} from "solid-js";
|
|
15
15
|
import type { AiActiveTurn } from "../client/projection";
|
|
16
|
+
import { aiModelChooserMessages } from "../model-chooser-messages";
|
|
16
17
|
import { type AiActiveTurnSegment, type AiTurnBlock, splitActiveTurnBlocks } from "../protocol";
|
|
17
18
|
import { type AiAssistantTimelineItem, buildAiMessageTimeline } from "../timeline";
|
|
18
19
|
import type { AiConversationTimelineEntry, AiStoredMessage, AiTurnError } from "../types";
|
|
@@ -257,7 +258,27 @@ const storedItems = (
|
|
|
257
258
|
actionDisplay: "menu",
|
|
258
259
|
anchorId: item.entry.seq,
|
|
259
260
|
};
|
|
260
|
-
|
|
261
|
+
// A removed model: the chat says once which model answers from now on, right before the message it took over.
|
|
262
|
+
const replacement = item.entry.meta?.modelReplacement;
|
|
263
|
+
const replacementItem: ChatTimelineItem[] = replacement
|
|
264
|
+
? [
|
|
265
|
+
{
|
|
266
|
+
kind: "message",
|
|
267
|
+
id: `${item.id}:model-replaced`,
|
|
268
|
+
role: "system",
|
|
269
|
+
createdAt: item.entry.createdAt,
|
|
270
|
+
content: (
|
|
271
|
+
<p class="text-xs text-dimmed">
|
|
272
|
+
<i class="ti ti-replace mr-1" aria-hidden="true" />
|
|
273
|
+
{aiModelChooserMessages
|
|
274
|
+
.resolve([locale])
|
|
275
|
+
.t.noteRemoved({ removed: replacement.previousLabel, next: replacement.nextLabel })}
|
|
276
|
+
</p>
|
|
277
|
+
),
|
|
278
|
+
},
|
|
279
|
+
]
|
|
280
|
+
: [];
|
|
281
|
+
return [...replacementItem, userItem, ...unansweredNotice(item.entry, itemIndex)];
|
|
261
282
|
}
|
|
262
283
|
|
|
263
284
|
if (item.type === "summary") {
|
package/src/ai/chat-quotas.ts
CHANGED
|
@@ -16,14 +16,15 @@ const usage = (b: AiQuotaBalance): AiChatQuotaBalance => {
|
|
|
16
16
|
export async function getAiChatQuotas(subject: AccessSubject): Promise<AiChatQuotaSnapshot> {
|
|
17
17
|
const config = await aiQuotas.config();
|
|
18
18
|
if (!config.enabled) return { enabled: false, balances: [] };
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
19
|
+
const state = await readAiSettingsState();
|
|
20
|
+
const models = await listAssistantAiModels(subject, undefined, state);
|
|
21
|
+
const snapshot = await aiQuotas.snapshot(subject, undefined, undefined, undefined, { state, config });
|
|
22
|
+
const profiles = state.profiles;
|
|
22
23
|
return {
|
|
23
24
|
enabled: snapshot.enabled,
|
|
24
25
|
unlimitedModels: models
|
|
25
26
|
.filter((model) => !hasBillableAiPricing(profiles.find((profile) => profile.id === model.id)?.pricing))
|
|
26
27
|
.map((model) => model.id),
|
|
27
|
-
balances: snapshot.balances.filter((b) => b.scope === "*" || models.some((m) => m.
|
|
28
|
+
balances: snapshot.balances.filter((b) => b.scope === "*" || models.some((m) => m.modelId === b.scope)).map(usage),
|
|
28
29
|
};
|
|
29
30
|
}
|
package/src/ai/chat-tasks.ts
CHANGED
|
@@ -1394,7 +1394,7 @@ export const aiChatTasks = {
|
|
|
1394
1394
|
|
|
1395
1395
|
deliverOccurrence: async (input: {
|
|
1396
1396
|
occurrenceId: string;
|
|
1397
|
-
|
|
1397
|
+
modelId: string;
|
|
1398
1398
|
runConfig: AiChatTurnRunConfig;
|
|
1399
1399
|
userMessage: Message;
|
|
1400
1400
|
expectedRevision: number;
|
|
@@ -1464,7 +1464,7 @@ export const aiChatTasks = {
|
|
|
1464
1464
|
"idx_ai_turns_conversation_short_id",
|
|
1465
1465
|
(attempt, shortId) => attempt<{ id: string }[]>`
|
|
1466
1466
|
INSERT INTO ai.turns (short_id, conversation_id, model_profile_id, status, run_config)
|
|
1467
|
-
VALUES (${shortId}, ${occurrence.conversation_id}::uuid, ${input.
|
|
1467
|
+
VALUES (${shortId}, ${occurrence.conversation_id}::uuid, ${input.modelId}, 'queued', (${JSON.stringify(runConfig)}::text)::jsonb)
|
|
1468
1468
|
RETURNING id
|
|
1469
1469
|
`,
|
|
1470
1470
|
);
|
|
@@ -2,6 +2,7 @@ import { type Accessor, createMemo, createSignal, onCleanup } from "solid-js";
|
|
|
2
2
|
import { createStore, reconcile } from "solid-js/store";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { CapabilityActionReceiptSchema } from "../../contracts/capabilities";
|
|
5
|
+
import type { AiModelRequest } from "../../shared/ai-models";
|
|
5
6
|
import { type AiActionCheck, preferredActionCheck } from "../action-review";
|
|
6
7
|
import { type AiAttachmentRef, aiAttachmentMarker } from "../attachments";
|
|
7
8
|
import { aiChatMessages } from "../chat/messages";
|
|
@@ -857,7 +858,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
857
858
|
};
|
|
858
859
|
|
|
859
860
|
const queuedAttempts = new Map<string, { key: string; files?: File[]; id: string }>();
|
|
860
|
-
const queueMessage = (input: ComposerDraftInput & {
|
|
861
|
+
const queueMessage = (input: ComposerDraftInput & { model?: AiModelRequest }): Promise<boolean> => {
|
|
861
862
|
if (!input.conversationId || !isComposerDraftSendable(input)) return Promise.resolve(false);
|
|
862
863
|
const conversationId = input.conversationId;
|
|
863
864
|
const key = JSON.stringify({
|
|
@@ -866,7 +867,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
866
867
|
draftContent: input.draftContent,
|
|
867
868
|
resources: input.resources,
|
|
868
869
|
storedFiles: input.storedFiles,
|
|
869
|
-
model: input.
|
|
870
|
+
model: input.model,
|
|
870
871
|
});
|
|
871
872
|
const previous = queuedAttempts.get(conversationId);
|
|
872
873
|
if (uncertainSubmissions()[conversationId] !== undefined) {
|
|
@@ -886,7 +887,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
886
887
|
const body = JSON.stringify({
|
|
887
888
|
draftRevision: saved.revision,
|
|
888
889
|
queueId,
|
|
889
|
-
|
|
890
|
+
model: input.model,
|
|
890
891
|
clientToolIds: options.clientToolIds?.filter((id) => options.frontendTools?.[id]),
|
|
891
892
|
});
|
|
892
893
|
queuedAttempts.set(conversationId, { key, files: input.files, id: queueId });
|
|
@@ -914,7 +915,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
914
915
|
});
|
|
915
916
|
};
|
|
916
917
|
|
|
917
|
-
const send = (input: ComposerDraftInput & {
|
|
918
|
+
const send = (input: ComposerDraftInput & { model?: AiModelRequest }): Promise<boolean> => {
|
|
918
919
|
if (!isComposerDraftSendable(input)) return Promise.resolve(false);
|
|
919
920
|
const targetConversation = input.conversationId ? Promise.resolve(input.conversationId) : ensureConversation();
|
|
920
921
|
return queueDraftOperation(async () => {
|
|
@@ -945,7 +946,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
945
946
|
kind: "message",
|
|
946
947
|
message: { role: "user", content: optimisticContent },
|
|
947
948
|
loopId: null,
|
|
948
|
-
|
|
949
|
+
modelId: null,
|
|
949
950
|
providerModel: null,
|
|
950
951
|
usage: null,
|
|
951
952
|
stopReason: null,
|
|
@@ -993,7 +994,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
993
994
|
const submissionBody = JSON.stringify({
|
|
994
995
|
draftRevision: savedDraft.revision,
|
|
995
996
|
clientToolIds: options.clientToolIds?.filter((id) => options.frontendTools?.[id]),
|
|
996
|
-
|
|
997
|
+
model: input.model,
|
|
997
998
|
});
|
|
998
999
|
submissionBodies.set(conversationId, submissionBody);
|
|
999
1000
|
const result = await request<SubmitTurnResult>(
|
|
@@ -1024,7 +1025,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
1024
1025
|
seq: 0,
|
|
1025
1026
|
status: "running",
|
|
1026
1027
|
blocks: [],
|
|
1027
|
-
|
|
1028
|
+
modelId: result.turn.modelId,
|
|
1028
1029
|
}),
|
|
1029
1030
|
);
|
|
1030
1031
|
if (state.conversation) {
|
|
@@ -1244,7 +1245,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
1244
1245
|
return requestPromise;
|
|
1245
1246
|
};
|
|
1246
1247
|
|
|
1247
|
-
const compactConversation = async (input: {
|
|
1248
|
+
const compactConversation = async (input: { model?: AiModelRequest } = {}) => {
|
|
1248
1249
|
const conversationId = activeConversationId();
|
|
1249
1250
|
if (!conversationId) {
|
|
1250
1251
|
setGlobalError("Open a chat before compacting context.");
|
|
@@ -1272,7 +1273,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
1272
1273
|
input: {
|
|
1273
1274
|
content?: AiUserContentPart[];
|
|
1274
1275
|
mode?: "retry" | "details" | "concise";
|
|
1275
|
-
|
|
1276
|
+
model?: AiModelRequest;
|
|
1276
1277
|
} = {},
|
|
1277
1278
|
) => {
|
|
1278
1279
|
const conversationId = activeConversationId();
|
|
@@ -1292,7 +1293,7 @@ export const createAiChatController = (options: CreateAiChatControllerOptions) =
|
|
|
1292
1293
|
body: JSON.stringify({
|
|
1293
1294
|
mode: input.mode ?? "retry",
|
|
1294
1295
|
content: input.content,
|
|
1295
|
-
|
|
1296
|
+
model: input.model,
|
|
1296
1297
|
}),
|
|
1297
1298
|
},
|
|
1298
1299
|
"AI retry failed",
|
|
@@ -8,7 +8,7 @@ export type AiActiveTurn = {
|
|
|
8
8
|
seq: number;
|
|
9
9
|
status: "running" | "waiting_for_action";
|
|
10
10
|
blocks: AiTurnBlock[];
|
|
11
|
-
|
|
11
|
+
modelId: string | null;
|
|
12
12
|
/**
|
|
13
13
|
* A model call failed transiently and waits for its retry. Live only: the next turn event ends it, and so does a
|
|
14
14
|
* snapshot that has moved past the retry.
|
|
@@ -124,7 +124,7 @@ export const activeTurnFromSnapshot = (snapshot: AiTurnSnapshot | null): AiActiv
|
|
|
124
124
|
seq: snapshot.seq,
|
|
125
125
|
status: snapshot.status === "waiting_for_action" ? "waiting_for_action" : "running",
|
|
126
126
|
blocks: normalizeCustomApprovalBlocks(snapshot.blocks),
|
|
127
|
-
|
|
127
|
+
modelId: snapshot.modelId,
|
|
128
128
|
createdAt: snapshot.createdAt,
|
|
129
129
|
...(snapshot.actionWaitMs !== undefined ? { actionWaitMs: snapshot.actionWaitMs } : {}),
|
|
130
130
|
...(snapshot.waitingSince !== undefined ? { waitingSince: snapshot.waitingSince } : {}),
|
|
@@ -191,7 +191,7 @@ export const reduceWireEvent = (state: AiChatProjection, event: AiWireEvent): Ai
|
|
|
191
191
|
seq: event.seq,
|
|
192
192
|
status: deriveStatus(blocks),
|
|
193
193
|
blocks,
|
|
194
|
-
|
|
194
|
+
modelId: event.modelId,
|
|
195
195
|
...turnTiming(active?.turnId === event.turnId ? active : null),
|
|
196
196
|
},
|
|
197
197
|
};
|
package/src/ai/compaction.ts
CHANGED
|
@@ -120,7 +120,7 @@ const buildCompactionPrompt = (additionalInstructions: string, source: string) =
|
|
|
120
120
|
export const createCloudCompactFn = (input: {
|
|
121
121
|
conversationId: string;
|
|
122
122
|
turnId?: string;
|
|
123
|
-
|
|
123
|
+
modelId: string;
|
|
124
124
|
additionalInstructions: string;
|
|
125
125
|
maxOutputTokens?: number;
|
|
126
126
|
signal: AbortSignal;
|
|
@@ -157,7 +157,7 @@ export const createCloudCompactFn = (input: {
|
|
|
157
157
|
task: "chat-compaction",
|
|
158
158
|
attribution: { conversationId: input.conversationId, turnId: input.turnId },
|
|
159
159
|
appId: "core",
|
|
160
|
-
|
|
160
|
+
modelId: input.modelId,
|
|
161
161
|
providerModel: ctx.provider.model,
|
|
162
162
|
};
|
|
163
163
|
let usage: Usage | undefined;
|
|
@@ -203,7 +203,7 @@ export const createCloudCompactFn = (input: {
|
|
|
203
203
|
conversationId: input.conversationId,
|
|
204
204
|
turnId: input.turnId,
|
|
205
205
|
checkpointSeq: checkpoint.seq,
|
|
206
|
-
|
|
206
|
+
modelId: input.modelId,
|
|
207
207
|
summary: {
|
|
208
208
|
role: "assistant",
|
|
209
209
|
content: [{ type: "text", text: `Conversation summary:\n${summaryText}` }],
|