@k2b/cloud 0.42.0 → 0.44.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 +13 -5
- package/scripts/app-favicon.ts +7 -6
- package/scripts/build.ts +3 -0
- package/scripts/installable-assets.ts +61 -0
- package/scripts/preload.ts +3 -0
- package/src/_internal/app-access-path.ts +12 -0
- 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 +185 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +10 -5
- package/src/_internal/define-app.ts +159 -45
- package/src/_internal/installable-assets.ts +90 -0
- package/src/_internal/installable-head.ts +22 -0
- 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/page-responses.ts +34 -3
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +15 -1
- package/src/_internal/registry.ts +44 -1
- package/src/_internal/runtime-context.ts +9 -1
- package/src/_internal/runtime-watcher.ts +8 -0
- package/src/_internal/status-preserving-ssr.ts +12 -1
- package/src/access/AppAccessEditor.browser-fixture.ts +429 -0
- package/src/access/AppAccessEditor.browser-frame.tsx +68 -0
- package/src/access/AppAccessEditor.browser-harness.tsx +65 -0
- package/src/access/AppAccessEditor.tsx +380 -0
- package/src/access/PermissionEditor.browser-harness.tsx +22 -2
- package/src/access/PermissionEditor.tsx +386 -77
- package/src/access/PrincipalAvatar.tsx +63 -0
- package/src/access/PrincipalPicker.tsx +29 -8
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-adapter.ts +51 -0
- package/src/access/app-access-messages.ts +172 -0
- package/src/access/messages.ts +14 -0
- package/src/access/ui.ts +4 -1
- package/src/account/Avatar.tsx +21 -3
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +8 -1
- 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/capability-execution.ts +5 -2
- 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/code-capability-routes.ts +1 -0
- 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 +4 -4
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +38 -27
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +11 -5
- package/src/ai/inference-calls.ts +6 -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-slots.ts +10 -2
- package/src/ai/openai-api.ts +83 -16
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/projects-routes.ts +2 -1
- package/src/ai/projects.ts +13 -12
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +138 -0
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider-retry.ts +1 -1
- package/src/ai/provider.ts +2 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +11 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas-migrate.ts +1 -0
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +6 -0
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +17 -18
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- 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 +29 -46
- 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 +37 -21
- package/src/ai/turn-failure.ts +24 -2
- 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 +40 -16
- package/src/api/admin-ai-quotas.ts +34 -56
- 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/admin-directory-shares.ts +55 -0
- package/src/api/admin-identity.ts +81 -2
- package/src/api/app-access.ts +85 -0
- 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 +26 -3
- package/src/api/capabilities.ts +127 -15
- package/src/api/capability-streams.ts +2 -5
- package/src/api/identity-child-invocation.ts +145 -0
- package/src/api/index.ts +11 -2
- package/src/api/mcp.ts +180 -5
- package/src/api/me.ts +38 -17
- package/src/api/pwa-phone.ts +87 -48
- 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/api/widgets.ts +1 -0
- package/src/app-pwa/authority.ts +75 -0
- package/src/app-pwa/contracts.ts +177 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +23 -0
- package/src/app-pwa/protocol.ts +27 -0
- package/src/app-pwa/schema.ts +59 -0
- package/src/app-pwa/service.ts +861 -0
- package/src/app-pwa/transaction.ts +338 -0
- package/src/app-pwa/transport-input.ts +57 -0
- package/src/app-pwa/transport.ts +235 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/app-access-recovery.ts +44 -0
- package/src/browser/app-session.ts +14 -3
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/live-websocket.ts +5 -0
- package/src/browser/pwa-drafts.ts +57 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/resource-picker.tsx +3 -2
- package/src/capabilities/calendar-sources.ts +302 -0
- package/src/capabilities/executions.ts +10 -3
- package/src/capabilities/migrate.ts +4 -0
- package/src/capabilities/nested.ts +43 -0
- package/src/capabilities/server-invoke.ts +202 -0
- package/src/capabilities/server.ts +26 -187
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/account.ts +57 -0
- package/src/cli/admin/agents.ts +66 -0
- 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 +7 -3
- package/src/cli/app-access.ts +60 -0
- package/src/cli/apps.ts +89 -26
- package/src/cli/avatar.ts +64 -0
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/cli/index.ts +1 -1
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +52 -5
- package/src/contracts/app-approval.ts +8 -1
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +24 -5
- package/src/contracts/calendar-source.ts +274 -0
- package/src/contracts/calendar-view.ts +35 -0
- package/src/contracts/capabilities.ts +164 -3
- package/src/contracts/geo-admin.ts +2 -0
- package/src/contracts/geo-route.ts +0 -11
- package/src/contracts/geo.ts +14 -2
- package/src/contracts/index.ts +6 -0
- package/src/contracts/installable.ts +40 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/profile.ts +32 -0
- package/src/contracts/pwa-builds.ts +14 -0
- package/src/contracts/pwa-location.ts +38 -0
- package/src/contracts/pwa.ts +24 -3
- package/src/contracts/registry.ts +8 -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 +4 -0
- 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 +2 -1
- package/src/server/api/respond.ts +8 -3
- package/src/server/index.ts +1 -0
- package/src/server/middleware/app-access.ts +7 -6
- package/src/server/middleware/auth.ts +168 -66
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +4 -2
- package/src/server/middleware/workload.ts +3 -3
- package/src/server/oauth-scope.ts +10 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +8 -0
- package/src/services/accounts/app.ts +35 -3
- package/src/services/accounts/avatar.ts +342 -35
- package/src/services/accounts/base-group.ts +4 -5
- package/src/services/accounts/directory-shares.ts +157 -0
- package/src/services/accounts/entities.ts +50 -6
- package/src/services/accounts/groups.ts +3 -1
- package/src/services/accounts/identities.ts +64 -12
- package/src/services/accounts/local-groups.ts +1 -1
- package/src/services/accounts/users.ts +23 -15
- package/src/services/app-access-management.ts +519 -0
- package/src/services/app-access.ts +25 -7
- package/src/services/app-approval.ts +15 -4
- 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/admin.ts +4 -1
- package/src/services/geo/ledger.ts +47 -14
- package/src/services/geo/route-chain.ts +40 -18
- package/src/services/geo/route-estimate.ts +19 -0
- package/src/services/geo/route-plan.ts +0 -2
- package/src/services/geo/route-service.ts +57 -39
- package/src/services/geo/service.ts +51 -25
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/index.ts +2 -1
- package/src/services/identity/invocation-actor.ts +50 -2
- package/src/services/identity/invocation-authority.ts +38 -1
- package/src/services/identity/invocation-token.ts +255 -50
- package/src/services/identity/issue-invocation.ts +9 -1
- package/src/services/identity/models-only-auth.ts +13 -4
- package/src/services/identity/nested-invocation.ts +128 -0
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +24 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/policy.ts +19 -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/service-account-credentials.ts +5 -2
- package/src/services/session/index.ts +14 -0
- package/src/services/settings/core-settings.ts +91 -47
- 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/index.ts +2 -0
- package/src/shared-text/documents.ts +25 -8
- package/src/shared-text/log.ts +1 -1
- package/src/shared-text/text-diff.ts +75 -0
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +11 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +114 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +1 -1
- package/src/ssr/AppPwaRuntime.island.tsx +82 -0
- package/src/ssr/CloudShell.island.tsx +25 -4
- package/src/ssr/Layout.tsx +15 -7
- package/src/ssr/PwaLayout.tsx +73 -8
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/PwaRuntime.island.tsx +85 -6
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/ShellRuntime.island.tsx +5 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +1 -1
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -20
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-pwa-document.tsx +16 -0
- package/src/ssr/app-pwa-messages.ts +17 -0
- package/src/ssr/index.ts +8 -0
- package/src/ssr/layout-context.ts +1 -1
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +9 -1
- package/src/ssr/pwa-messages.ts +10 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +2 -0
- 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/runtime/executor.ts +132 -33
- 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/transcription.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { openAICompatibleTranscription, type TranscriptionProvider } from "@k2b/nessi/ai";
|
|
2
|
-
import { coreSettings } from "../services";
|
|
3
2
|
import { logger, type TraceContext, trace } from "../services/logging";
|
|
4
3
|
import { AI_AUDIO_MAX_BYTES, inspectAiAudio } from "./audio-format";
|
|
5
4
|
import { getAiCredential } from "./credentials";
|
|
6
5
|
import { beginAiCall, finishAiCall } from "./inference-calls";
|
|
7
|
-
import {
|
|
6
|
+
import { classifyProviderFailure, recordAiProviderStatus } from "./provider-connections";
|
|
7
|
+
import { readAiSettingsState, resolveAiModelRef, selectAiModelProfile } from "./settings";
|
|
8
8
|
import { type AiUsageAttribution, safelyRecordStructuredRun } from "./structured-runs";
|
|
9
9
|
import type { AiDataBoundary, AiModelProfile } from "./types";
|
|
10
10
|
|
|
11
|
-
export const AI_AUDIO_MODEL_SETTING_KEY = "ai.audio_model_id";
|
|
12
11
|
/** Matches the existing chat turn execution budget; callers may cancel sooner. */
|
|
13
12
|
export const AI_TRANSCRIPTION_TIMEOUT_MS = 10 * 60_000;
|
|
14
13
|
|
|
@@ -31,31 +30,48 @@ export const createAiTranscriptionProvider = (profile: AiModelProfile, apiKey?:
|
|
|
31
30
|
"transcription_configuration_failed",
|
|
32
31
|
"Audio transcription is not set up: the audio model has no base URL. An administrator can set the audio provider URL in the AI settings.",
|
|
33
32
|
);
|
|
34
|
-
|
|
33
|
+
const provider = openAICompatibleTranscription(profile.model, { name: profile.provider, baseURL, apiKey });
|
|
34
|
+
return {
|
|
35
|
+
name: provider.name,
|
|
36
|
+
model: provider.model,
|
|
37
|
+
transcribe: async (request) => {
|
|
38
|
+
try {
|
|
39
|
+
const result = await provider.transcribe(request);
|
|
40
|
+
await recordAiProviderStatus(profile.providerId, { status: "ok", message: "The provider connection works." });
|
|
41
|
+
return result;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const failure = describeTranscriptionFailure(error, "provider", request.signal?.aborted);
|
|
44
|
+
if (!request.signal?.aborted)
|
|
45
|
+
await recordAiProviderStatus(
|
|
46
|
+
profile.providerId,
|
|
47
|
+
failure.httpStatus
|
|
48
|
+
? classifyProviderFailure({ status: failure.httpStatus })
|
|
49
|
+
: failure.code === "transcription_connection_failed"
|
|
50
|
+
? classifyProviderFailure(new TypeError())
|
|
51
|
+
: classifyProviderFailure(error),
|
|
52
|
+
);
|
|
53
|
+
throw failure;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
35
57
|
};
|
|
36
58
|
|
|
37
59
|
export const resolveAiAudioModel = async (
|
|
38
|
-
input: {
|
|
60
|
+
input: { model?: import("../shared/ai-models").AiModelRequest; allowedDataBoundaries?: AiDataBoundary[] } = {},
|
|
39
61
|
): Promise<AiResolvedAudioModel> => {
|
|
40
62
|
const state = await readAiSettingsState();
|
|
41
|
-
if (!state.ok)
|
|
42
|
-
throw Object.assign(
|
|
43
|
-
new AiTranscriptionError(
|
|
44
|
-
"transcription_configuration_failed",
|
|
45
|
-
`Audio transcription is not available: ${state.error.message} An administrator can fix this in the AI settings.`,
|
|
46
|
-
),
|
|
47
|
-
{ aiError: state.error },
|
|
48
|
-
);
|
|
49
63
|
if (!state.enabled)
|
|
50
64
|
throw new AiTranscriptionError(
|
|
51
65
|
"transcription_configuration_failed",
|
|
52
66
|
"Audio transcription is not available: AI is disabled. An administrator can enable AI in the AI settings.",
|
|
53
67
|
);
|
|
54
|
-
const modelId = input.
|
|
68
|
+
const modelId = input.model
|
|
69
|
+
? resolveAiModelRef(state, input.model).id
|
|
70
|
+
: state.assignments.find((assignment) => assignment.slot === "audio")?.variantId;
|
|
55
71
|
if (!modelId)
|
|
56
72
|
throw new AiTranscriptionError(
|
|
57
73
|
"transcription_configuration_failed",
|
|
58
|
-
"Audio transcription is not set up: no audio model is
|
|
74
|
+
"Audio transcription is not set up: no audio model is assigned. An administrator can configure an audio model in the AI settings.",
|
|
59
75
|
);
|
|
60
76
|
let profile: AiModelProfile;
|
|
61
77
|
try {
|
|
@@ -82,7 +98,7 @@ export const resolveAiAudioModel = async (
|
|
|
82
98
|
error instanceof Error && "aiError" in error ? { aiError: error.aiError } : {},
|
|
83
99
|
);
|
|
84
100
|
}
|
|
85
|
-
const credential = await getAiCredential(profile.
|
|
101
|
+
const credential = await getAiCredential(profile.providerId);
|
|
86
102
|
if (profile.provider === "openai" && !credential?.trim())
|
|
87
103
|
throw new AiTranscriptionError(
|
|
88
104
|
"transcription_configuration_failed",
|
|
@@ -97,7 +113,7 @@ export type RunAiTranscriptionInput = {
|
|
|
97
113
|
filename: string;
|
|
98
114
|
language?: string;
|
|
99
115
|
prompt?: string;
|
|
100
|
-
|
|
116
|
+
model?: import("../shared/ai-models").AiModelRequest;
|
|
101
117
|
allowedDataBoundaries?: AiDataBoundary[];
|
|
102
118
|
signal?: AbortSignal;
|
|
103
119
|
appId?: string;
|
|
@@ -108,7 +124,7 @@ export type RunAiTranscriptionInput = {
|
|
|
108
124
|
};
|
|
109
125
|
|
|
110
126
|
/** One provider call. Durable execution and input authorization belong to the caller. */
|
|
111
|
-
export const runAiTranscription = async (input: RunAiTranscriptionInput): Promise<{ text: string;
|
|
127
|
+
export const runAiTranscription = async (input: RunAiTranscriptionInput): Promise<{ text: string; modelId: string }> => {
|
|
112
128
|
return trace.withSpan(
|
|
113
129
|
{
|
|
114
130
|
name: `ai.transcription.${input.task}`,
|
|
@@ -154,13 +170,13 @@ export const runAiTranscription = async (input: RunAiTranscriptionInput): Promis
|
|
|
154
170
|
});
|
|
155
171
|
signal.throwIfAborted();
|
|
156
172
|
succeeded = true;
|
|
157
|
-
return { text: result.text,
|
|
173
|
+
return { text: result.text, modelId: resolved.profile.id };
|
|
158
174
|
} catch (error) {
|
|
159
175
|
failure = describeTranscriptionFailure(error, stage, signal.aborted);
|
|
160
176
|
logger("ai:transcription")[input.signal?.aborted ? "info" : "error"](failure.message, {
|
|
161
177
|
traceId: span.traceId,
|
|
162
178
|
task: input.task,
|
|
163
|
-
|
|
179
|
+
modelId: resolved?.profile.id ?? input.model,
|
|
164
180
|
providerModel: resolved?.profile.model,
|
|
165
181
|
stage,
|
|
166
182
|
errorCode: failure.code,
|
|
@@ -180,7 +196,7 @@ export const runAiTranscription = async (input: RunAiTranscriptionInput): Promis
|
|
|
180
196
|
task: input.task,
|
|
181
197
|
appId: input.appId,
|
|
182
198
|
attribution: input.attribution,
|
|
183
|
-
|
|
199
|
+
modelId: resolved?.profile.id,
|
|
184
200
|
providerModel: resolved?.profile.model,
|
|
185
201
|
mode: "transcription",
|
|
186
202
|
attempts: resolved ? 1 : 0,
|
package/src/ai/turn-failure.ts
CHANGED
|
@@ -13,8 +13,9 @@ export class AiTurnFailure extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* Why a turn failed. `error` is what a person reads, worded by the reader's client; `detail` is the
|
|
17
|
-
* only the log keeps. `message`, when set, is Cloud's
|
|
16
|
+
* Why a turn failed. `error` is what a person reads, worded by the reader's client; `detail` is the diagnostic cause, which
|
|
17
|
+
* only the log keeps. Provider diagnostics have already been reduced to fixed text. `message`, when set, is Cloud's
|
|
18
|
+
* own wording for the stored error instead of the worded reason.
|
|
18
19
|
*/
|
|
19
20
|
export type AiTurnFailureInfo = { error: AiTurnError; detail: string; message?: string };
|
|
20
21
|
|
|
@@ -102,3 +103,24 @@ export const rememberProviderErrors = (provider: Provider, remember: (reason: Ai
|
|
|
102
103
|
},
|
|
103
104
|
};
|
|
104
105
|
};
|
|
106
|
+
|
|
107
|
+
/** nessi may construct issue messages from model tool arguments or names after the adapter returns. */
|
|
108
|
+
export const aiIssueDiagnostic = (issue: NessiIssue): { code: string; message: string } => {
|
|
109
|
+
switch (issue.kind) {
|
|
110
|
+
case "provider_error":
|
|
111
|
+
return { code: "provider_error", message: "AI provider request failed." };
|
|
112
|
+
case "timeout":
|
|
113
|
+
return issue.scope === "tool"
|
|
114
|
+
? { code: "tool_timeout", message: "AI tool execution timed out." }
|
|
115
|
+
: { code: "provider_timeout", message: "AI provider request timed out." };
|
|
116
|
+
case "malformed_tool_call":
|
|
117
|
+
case "cancelled_tool_call":
|
|
118
|
+
return { code: issue.kind, message: "AI provider returned an incomplete or invalid tool call." };
|
|
119
|
+
case "tool_execution_error":
|
|
120
|
+
return { code: "tool_execution_error", message: "AI tool execution failed." };
|
|
121
|
+
case "tool_historical_result_error":
|
|
122
|
+
return { code: "tool_historical_result_error", message: "AI tool history could not be prepared." };
|
|
123
|
+
default:
|
|
124
|
+
return { code: "runtime_error", message: "AI execution failed." };
|
|
125
|
+
}
|
|
126
|
+
};
|
package/src/ai/types.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
CloudResourceRef,
|
|
21
21
|
} from "../contracts/capabilities";
|
|
22
22
|
import type { RequestActor } from "../server";
|
|
23
|
+
import type { AiModelRequest, AiResolvedModelRef } from "../shared/ai-models";
|
|
23
24
|
import type { AiActionCheck, AiStoredActionReview } from "./action-review";
|
|
24
25
|
import type { AiLocalClient, AiLocalContext, AiLocalToolName } from "./local-tool-contracts";
|
|
25
26
|
import type { AiTurnBlock } from "./protocol";
|
|
@@ -27,6 +28,8 @@ import type { AiTurnBlock } from "./protocol";
|
|
|
27
28
|
/** Current reader-confirmed metadata for a recently sent resource. */
|
|
28
29
|
export type AiRecentResource = {
|
|
29
30
|
ref: CloudResourceRef;
|
|
31
|
+
context?: string;
|
|
32
|
+
status?: string;
|
|
30
33
|
title: string;
|
|
31
34
|
icon?: string;
|
|
32
35
|
href?: string;
|
|
@@ -44,7 +47,7 @@ export type AiRecentSentResourceRef = {
|
|
|
44
47
|
conversationId: string;
|
|
45
48
|
};
|
|
46
49
|
|
|
47
|
-
export const AI_MODEL_CAPABILITIES = ["
|
|
50
|
+
export const AI_MODEL_CAPABILITIES = ["tools", "vision", "transcription"] as const;
|
|
48
51
|
export type AiModelCapability = (typeof AI_MODEL_CAPABILITIES)[number];
|
|
49
52
|
|
|
50
53
|
export const AI_IMAGE_MEDIA_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/gif"] as const;
|
|
@@ -62,11 +65,15 @@ export type AiDataPolicy = AiDataBoundary;
|
|
|
62
65
|
export type AiModelProfile = {
|
|
63
66
|
id: string;
|
|
64
67
|
label: string;
|
|
68
|
+
modelId: string;
|
|
69
|
+
providerId: string;
|
|
70
|
+
variantLabel: string;
|
|
71
|
+
description: string;
|
|
72
|
+
icon: import("../shared/ai-models").AiModelIcon;
|
|
73
|
+
showInPicker: boolean;
|
|
65
74
|
provider: AiProviderId;
|
|
66
75
|
model: string;
|
|
67
76
|
enabled: boolean;
|
|
68
|
-
/** Small logo (data URL, hard-compressed) shown in the admin card and the composer model picker. */
|
|
69
|
-
image?: string;
|
|
70
77
|
capabilities: AiModelCapability[];
|
|
71
78
|
dataBoundary: AiDataBoundary;
|
|
72
79
|
baseURL?: string;
|
|
@@ -82,67 +89,34 @@ export type AiModelProfile = {
|
|
|
82
89
|
pricing?: import("../shared/ai-costs").AiModelPricing;
|
|
83
90
|
};
|
|
84
91
|
|
|
85
|
-
export type AiPublicModelProfile = Pick<
|
|
86
|
-
AiModelProfile,
|
|
87
|
-
"id" | "label" | "provider" | "model" | "image" | "capabilities" | "dataBoundary" | "contextWindow"
|
|
88
|
-
>;
|
|
89
|
-
|
|
90
92
|
export type AiUserContentPart = ContentPart;
|
|
91
93
|
|
|
92
|
-
export type AiSettingsErrorCode =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
fields?: Record<string, string>;
|
|
94
|
+
export type AiSettingsErrorCode = "ai_disabled" | "ai_no_model" | "model_policy_mismatch";
|
|
95
|
+
export type AiSettingsError = { code: AiSettingsErrorCode; message: string; fields?: Record<string, string> };
|
|
96
|
+
export type AiSettingsState = {
|
|
97
|
+
ok: true;
|
|
98
|
+
enabled: boolean;
|
|
99
|
+
assignments: import("../shared/ai-models").AiModelAssignment[];
|
|
100
|
+
globalInstructions: string;
|
|
101
|
+
localClientInstructions: string;
|
|
102
|
+
compactionInstructions: string;
|
|
103
|
+
maxToolResultChars: number;
|
|
104
|
+
firecrawlConfigured: boolean;
|
|
105
|
+
profiles: AiModelProfile[];
|
|
105
106
|
};
|
|
106
107
|
|
|
107
|
-
export type AiSettingsState =
|
|
108
|
-
| {
|
|
109
|
-
ok: true;
|
|
110
|
-
enabled: boolean;
|
|
111
|
-
defaultModelId: string;
|
|
112
|
-
visionModelId?: string;
|
|
113
|
-
globalInstructions: string;
|
|
114
|
-
localClientInstructions: string;
|
|
115
|
-
compactionInstructions: string;
|
|
116
|
-
maxToolResultChars: number;
|
|
117
|
-
firecrawlConfigured: boolean;
|
|
118
|
-
profiles: AiModelProfile[];
|
|
119
|
-
}
|
|
120
|
-
| {
|
|
121
|
-
ok: false;
|
|
122
|
-
enabled: boolean;
|
|
123
|
-
defaultModelId: string;
|
|
124
|
-
visionModelId?: string;
|
|
125
|
-
globalInstructions: string;
|
|
126
|
-
localClientInstructions: string;
|
|
127
|
-
compactionInstructions: string;
|
|
128
|
-
maxToolResultChars: number;
|
|
129
|
-
firecrawlConfigured: boolean;
|
|
130
|
-
profiles: AiModelProfile[];
|
|
131
|
-
error: AiSettingsError;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
108
|
export type AiModelPolicy =
|
|
135
109
|
| { kind: "platform-default"; allowedDataBoundaries?: AiDataBoundary[]; requiredCapabilities?: AiModelCapability[] }
|
|
136
110
|
| { kind: "locked"; modelId: string; allowedDataBoundaries?: AiDataBoundary[]; requiredCapabilities?: AiModelCapability[] }
|
|
137
111
|
| {
|
|
138
112
|
kind: "selectable";
|
|
139
|
-
defaultModelId?: string;
|
|
140
113
|
allowedModelIds?: string[];
|
|
141
114
|
allowedDataBoundaries?: AiDataBoundary[];
|
|
142
115
|
requiredCapabilities?: AiModelCapability[];
|
|
143
116
|
};
|
|
144
117
|
|
|
145
118
|
export type AiResolvedModel = {
|
|
119
|
+
model: import("../shared/ai-models").AiResolvedModelRef;
|
|
146
120
|
profile: AiModelProfile;
|
|
147
121
|
provider: Provider;
|
|
148
122
|
};
|
|
@@ -230,7 +204,7 @@ export type AiEnrichmentRun = {
|
|
|
230
204
|
conversationId: string;
|
|
231
205
|
status: AiEnrichmentRunStatus;
|
|
232
206
|
trigger: AiEnrichmentTrigger;
|
|
233
|
-
|
|
207
|
+
modelId: string | null;
|
|
234
208
|
/** nessi structured mode of the successful call: native | fallback | repair. */
|
|
235
209
|
mode: string | null;
|
|
236
210
|
durationMs: number | null;
|
|
@@ -298,7 +272,7 @@ export type AiStoredMessage = {
|
|
|
298
272
|
kind: "message" | "summary";
|
|
299
273
|
message: Message;
|
|
300
274
|
loopId: string | null;
|
|
301
|
-
|
|
275
|
+
modelId: string | null;
|
|
302
276
|
providerModel: string | null;
|
|
303
277
|
usage: Usage | null;
|
|
304
278
|
stopReason: string | null;
|
|
@@ -313,6 +287,9 @@ export type AiStoredMessage = {
|
|
|
313
287
|
meta: {
|
|
314
288
|
/** The atomically consumed composer revision, used to confirm a local send. */
|
|
315
289
|
submittedDraftRevision?: number;
|
|
290
|
+
modelRequest?: AiModelRequest;
|
|
291
|
+
modelResolved?: AiResolvedModelRef;
|
|
292
|
+
modelReplacement?: { kind: "model_replaced"; previousLabel: string; nextLabel: string };
|
|
316
293
|
compactedCount?: number;
|
|
317
294
|
todoPlan?: import("./todo-contracts").AiTodoPlan;
|
|
318
295
|
steerId?: string;
|
|
@@ -353,6 +330,7 @@ export type AiConversationTimelineEntry = {
|
|
|
353
330
|
id: string;
|
|
354
331
|
seq: number;
|
|
355
332
|
loopId: string | null;
|
|
333
|
+
modelReplacement?: { kind: "model_replaced"; previousLabel: string; nextLabel: string };
|
|
356
334
|
userPreview: string;
|
|
357
335
|
assistantPreview: string;
|
|
358
336
|
isSteer: boolean;
|
|
@@ -418,7 +396,7 @@ export type AiTurn = {
|
|
|
418
396
|
conversationId: string;
|
|
419
397
|
status: AiTurnStatus;
|
|
420
398
|
attempt: number;
|
|
421
|
-
|
|
399
|
+
modelId: string | null;
|
|
422
400
|
createdAt: string;
|
|
423
401
|
completedAt: string | null;
|
|
424
402
|
error: string | null;
|
|
@@ -534,7 +512,7 @@ export type AiProjectPromptSnapshot = {
|
|
|
534
512
|
instructions: string;
|
|
535
513
|
context: string;
|
|
536
514
|
references: CloudResourceRef[];
|
|
537
|
-
|
|
515
|
+
defaultModel: import("../shared/ai-models").AiModelRequest | null;
|
|
538
516
|
};
|
|
539
517
|
|
|
540
518
|
export type AiProjectFileToolStat = {
|
|
@@ -569,6 +547,8 @@ export type AiSkillFileToolSource = {
|
|
|
569
547
|
|
|
570
548
|
export type AiConversationResourceRef = {
|
|
571
549
|
ref: CloudResourceRef;
|
|
550
|
+
context?: string | null;
|
|
551
|
+
status?: string | null;
|
|
572
552
|
title: string | null;
|
|
573
553
|
preview: string | null;
|
|
574
554
|
icon: string | null;
|
|
@@ -581,6 +561,8 @@ export type AiConversationResourceRef = {
|
|
|
581
561
|
|
|
582
562
|
export type AiConversationResourceObservation = {
|
|
583
563
|
ref: CloudResourceRef;
|
|
564
|
+
context?: string;
|
|
565
|
+
status?: string;
|
|
584
566
|
title?: string;
|
|
585
567
|
preview?: string;
|
|
586
568
|
icon?: string;
|
|
@@ -601,6 +583,8 @@ export type AiConversationResourceOccurrence = AiConversationResourceRef & {
|
|
|
601
583
|
export type AiConversationSourceKind = "result" | "web" | "file" | "resource" | "activity";
|
|
602
584
|
|
|
603
585
|
export type AiConversationSource = {
|
|
586
|
+
context?: string | null;
|
|
587
|
+
status?: string | null;
|
|
604
588
|
kind: AiConversationSourceKind;
|
|
605
589
|
key: string;
|
|
606
590
|
title: string;
|
|
@@ -683,7 +667,10 @@ export type AiChatTurnRunConfig = {
|
|
|
683
667
|
/** Request locale persisted with the turn so async execution keeps the caller preference. */
|
|
684
668
|
locale?: string;
|
|
685
669
|
modelPolicy?: AiModelPolicy;
|
|
686
|
-
|
|
670
|
+
model?: AiModelRequest;
|
|
671
|
+
modelResolved?: AiResolvedModelRef;
|
|
672
|
+
modelLabel?: string;
|
|
673
|
+
modelReplacement?: { kind: "model_replaced"; previousLabel: string; nextLabel: string };
|
|
687
674
|
/** Optional instructions that apply only to this turn. */
|
|
688
675
|
systemPrompt?: string;
|
|
689
676
|
project?: AiProjectPromptSnapshot;
|
|
@@ -708,7 +695,8 @@ export type AiCompactionTurnRunConfig = {
|
|
|
708
695
|
kind: "compact";
|
|
709
696
|
actor?: RequestActor;
|
|
710
697
|
modelPolicy?: AiModelPolicy;
|
|
711
|
-
|
|
698
|
+
model?: AiModelRequest;
|
|
699
|
+
modelResolved?: AiResolvedModelRef;
|
|
712
700
|
};
|
|
713
701
|
|
|
714
702
|
export type AiTurnRunConfig = AiChatTurnRunConfig | AiCompactionTurnRunConfig;
|
|
@@ -851,7 +839,7 @@ export type AiConversationService = {
|
|
|
851
839
|
failInterChatMessage(input: { messageId: string; error: string }): Promise<boolean>;
|
|
852
840
|
deliverInterChatMessage(input: {
|
|
853
841
|
messageId: string;
|
|
854
|
-
|
|
842
|
+
modelId: string;
|
|
855
843
|
runConfig: AiChatTurnRunConfig;
|
|
856
844
|
userMessage: Message;
|
|
857
845
|
sourceHref?: string;
|
|
@@ -909,7 +897,7 @@ export type AiConversationService = {
|
|
|
909
897
|
conversationId: string;
|
|
910
898
|
status: AiEnrichmentRunStatus;
|
|
911
899
|
trigger: AiEnrichmentTrigger;
|
|
912
|
-
|
|
900
|
+
modelId?: string;
|
|
913
901
|
mode?: string;
|
|
914
902
|
durationMs?: number;
|
|
915
903
|
titleUpdated?: boolean;
|
|
@@ -954,7 +942,7 @@ export type AiConversationService = {
|
|
|
954
942
|
turnId?: string;
|
|
955
943
|
checkpointSeq: number;
|
|
956
944
|
summary: Message;
|
|
957
|
-
|
|
945
|
+
modelId?: string | null;
|
|
958
946
|
}): Promise<void>;
|
|
959
947
|
listTurnMessages(input: { conversationId: string; loopId: string; includeCompacted?: boolean }): Promise<AiStoredMessage[]>;
|
|
960
948
|
setMessageFeedback(input: {
|
|
@@ -964,12 +952,12 @@ export type AiConversationService = {
|
|
|
964
952
|
}): Promise<AiMessageFeedback | null>;
|
|
965
953
|
clearMessageFeedback(input: { conversationId: string; messageShortId: string }): Promise<boolean>;
|
|
966
954
|
/** Create a queued compaction turn. Chat turns must use submitChatTurn. */
|
|
967
|
-
createCompactionTurn(input: { conversationId: string;
|
|
955
|
+
createCompactionTurn(input: { conversationId: string; modelId: string; runConfig: AiCompactionTurnRunConfig }): Promise<AiTurn>;
|
|
968
956
|
/** Persist the user message and create its turn in one transaction. */
|
|
969
957
|
submitChatTurn(input: {
|
|
970
958
|
queuedMessageId?: string;
|
|
971
959
|
conversationId: string;
|
|
972
|
-
|
|
960
|
+
modelId: string;
|
|
973
961
|
runConfig: AiChatTurnRunConfig;
|
|
974
962
|
userMessage: Message;
|
|
975
963
|
/** Atomically consume this saved draft revision when present. */
|
|
@@ -984,7 +972,7 @@ export type AiConversationService = {
|
|
|
984
972
|
retrySourceTurnId?: string;
|
|
985
973
|
}): Promise<{ turn: AiTurn; message: AiStoredMessage }>;
|
|
986
974
|
getTurnRunConfig(input: { conversationId: string; turnId: string }): Promise<AiTurnRunConfig | null>;
|
|
987
|
-
getLatestTurn(input: { conversationId: string }): Promise<AiTurn | null>;
|
|
975
|
+
getLatestTurn(input: { conversationId: string; kind?: AiTurnRunConfig["kind"] }): Promise<AiTurn | null>;
|
|
988
976
|
getTurn(input: { conversationId: string; turnId: string }): Promise<AiTurn | null>;
|
|
989
977
|
getTurnByShortId(input: { conversationId: string; shortId: string }): Promise<AiTurn | null>;
|
|
990
978
|
getActiveTurn(input: { conversationId: string }): Promise<{
|
|
@@ -1076,7 +1064,7 @@ export type AiConversationService = {
|
|
|
1076
1064
|
/** Called after a durable append, for incremental usage/history projection. */
|
|
1077
1065
|
onMessage?: (message: AiStoredMessage) => Promise<void>;
|
|
1078
1066
|
conversationId: string;
|
|
1079
|
-
|
|
1067
|
+
modelId?: string | null;
|
|
1080
1068
|
turnId?: string | null;
|
|
1081
1069
|
leaseOwner?: string | null;
|
|
1082
1070
|
/** Ephemeral provider input for this turn; user-message persistence remains reference-only. */
|
package/src/ai/ui.tsx
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
export { AiTurnBlockView } from "./chat/blocks";
|
|
2
|
+
export { AiModelCard, type AiModelCardStop, aiModelClassLabel, aiModelClassSubtitle } from "./model-card";
|
|
3
|
+
export {
|
|
4
|
+
AiModelChooser,
|
|
5
|
+
type AiModelChooserState,
|
|
6
|
+
AiModelField,
|
|
7
|
+
AiModelNote,
|
|
8
|
+
aiModelBlocksSending,
|
|
9
|
+
aiModelRequestFor,
|
|
10
|
+
aiModelUnavailableText,
|
|
11
|
+
resolveAiModelChoice,
|
|
12
|
+
} from "./model-chooser";
|
|
13
|
+
export { aiModelChooserMessages } from "./model-chooser-messages";
|
|
14
|
+
export { AiModelIcon, aiModelDisplayIcon } from "./model-icon";
|
|
2
15
|
|
|
3
16
|
import { latestLoopUsage, latestUsage, latestUsageSnapshot, textFromMessage } from "./chat/message-utils";
|
|
4
17
|
|
package/src/ai/usage.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type AiUsageRun = {
|
|
|
12
12
|
createdAt: string;
|
|
13
13
|
userId: string | null;
|
|
14
14
|
userLabel: string | null;
|
|
15
|
-
|
|
15
|
+
modelId: string | null;
|
|
16
16
|
providerModel: string | null;
|
|
17
17
|
appId: string | null;
|
|
18
18
|
conversationId: string | null;
|
|
@@ -42,7 +42,7 @@ export type AiUsageFeedback = {
|
|
|
42
42
|
conversationTitle: string;
|
|
43
43
|
userId: string | null;
|
|
44
44
|
userLabel: string | null;
|
|
45
|
-
|
|
45
|
+
modelId: string | null;
|
|
46
46
|
providerModel: string | null;
|
|
47
47
|
rating: "up" | "down";
|
|
48
48
|
reasons: string[];
|
|
@@ -137,7 +137,7 @@ const events = (since: Date, until: Date) => sql`
|
|
|
137
137
|
`;
|
|
138
138
|
const globalFilter = (q: AiUsageQuery) => sql`
|
|
139
139
|
(${q.userId ?? null}::text IS NULL OR (${q.userId === "unassigned"} AND e.user_id IS NULL) OR e.user_id::text=${q.userId ?? null})
|
|
140
|
-
AND (${q.
|
|
140
|
+
AND (${q.modelId ?? null}::text IS NULL OR e.model_profile_id=${q.modelId ?? null})
|
|
141
141
|
AND (${q.providerModel ?? null}::text IS NULL OR e.provider_model=${q.providerModel ?? null})
|
|
142
142
|
AND (${q.workflowId ?? null}::uuid IS NULL OR e.workflow_id=${q.workflowId ?? null}::uuid)
|
|
143
143
|
AND (${q.workflowRunId ?? null}::uuid IS NULL OR e.workflow_run_id=${q.workflowRunId ?? null}::uuid)
|
|
@@ -187,7 +187,7 @@ const pageOf = async <T>(projection: ReturnType<typeof events>, q: AiUsageQuery)
|
|
|
187
187
|
return { items, total, page, perPage: q.perPage };
|
|
188
188
|
};
|
|
189
189
|
const runColumns = () => sql`e.id, e.kind, e.task, e.status, e.created_at::text AS "createdAt", e.user_id::text AS "userId",
|
|
190
|
-
COALESCE(NULLIF(u.display_name,''),u.uid,sa.name) AS "userLabel",e.model_profile_id AS "
|
|
190
|
+
COALESCE(NULLIF(u.display_name,''),u.uid,sa.name) AS "userLabel",e.model_profile_id AS "modelId",e.provider_model AS "providerModel",
|
|
191
191
|
e.app_id AS "appId",e.conversation_id::text AS "conversationId",e.turn_id::text AS "turnId",e.workflow_run_id::text AS "workflowRunId",
|
|
192
192
|
e.workflow_id::text AS "workflowId",e.workflow_name AS "workflowName",e.estimated,e.trace_id AS "traceId",e.tokens,e.cost::float8 AS cost,e.duration_ms AS "durationMs",e.error_code AS "errorCode",e.error,e.attempts,
|
|
193
193
|
e.request_started_at::text AS "requestStartedAt",e.generation_ms AS "generationMs",e.headers_ms AS "headersMs",e.first_byte_ms AS "firstByteMs",
|
|
@@ -235,7 +235,7 @@ export const aiUsage = {
|
|
|
235
235
|
// The response period is authoritative for both summary and feedback. Editing a rating never moves its response into a different period.
|
|
236
236
|
const feedbackRows = sql`SELECT m.id::text AS id,c.id::text AS "conversationId",c.title AS "conversationTitle",
|
|
237
237
|
e.user_id::text AS "userId",COALESCE(NULLIF(u.display_name,''),u.uid,sa.name) AS "userLabel",
|
|
238
|
-
e.model_profile_id AS "
|
|
238
|
+
e.model_profile_id AS "modelId",e.provider_model AS "providerModel",
|
|
239
239
|
CASE WHEN m.feedback_rating=1 THEN 'up' ELSE 'down' END AS rating,
|
|
240
240
|
m.feedback_reasons AS reasons,m.feedback_comment AS comment,m.created_at::text AS "createdAt",m.feedback_updated_at::text AS "updatedAt"
|
|
241
241
|
FROM (${filtered}) e JOIN ai.messages m ON m.loop_id=e.turn_id::text AND m.conversation_id=e.conversation_id
|
|
@@ -266,7 +266,7 @@ export const aiUsage = {
|
|
|
266
266
|
FROM ai.conversations c WHERE c.created_at >= ${since} AND c.created_at <= ${until} AND c.launched_by_app_id IS NOT NULL
|
|
267
267
|
AND (${q.userId ?? null}::text IS NULL OR (${q.userId === "unassigned"} AND c.created_by_user_id IS NULL) OR c.created_by_user_id::text=${q.userId ?? null})
|
|
268
268
|
AND (${q.appId ?? null}::text IS NULL OR c.launched_by_app_id=${q.appId ?? null})
|
|
269
|
-
AND ((${q.
|
|
269
|
+
AND ((${q.modelId ?? null}::text IS NULL AND ${q.providerModel ?? null}::text IS NULL) OR EXISTS (
|
|
270
270
|
SELECT 1 FROM (${filtered}) e WHERE e.kind='chat' AND e.conversation_id=c.id
|
|
271
271
|
)) GROUP BY c.launched_by_app_id ORDER BY chats DESC,c.launched_by_app_id`,
|
|
272
272
|
q,
|
|
@@ -305,12 +305,12 @@ export const aiUsage = {
|
|
|
305
305
|
LEFT JOIN auth.users u ON u.id=e.user_id LEFT JOIN auth.service_accounts sa ON sa.id=e.user_id WHERE e.kind=${kind} AND e.id=${id}`;
|
|
306
306
|
return row ?? null;
|
|
307
307
|
},
|
|
308
|
-
facets: async (field: "userId" | "
|
|
308
|
+
facets: async (field: "userId" | "modelId" | "providerModel" | "appId" | "task", search: string, input: Partial<AiUsageQuery>) => {
|
|
309
309
|
const { query, since, until } = period(input);
|
|
310
310
|
const key =
|
|
311
311
|
field === "userId"
|
|
312
312
|
? sql`e.user_id::text`
|
|
313
|
-
: field === "
|
|
313
|
+
: field === "modelId"
|
|
314
314
|
? sql`e.model_profile_id`
|
|
315
315
|
: field === "providerModel"
|
|
316
316
|
? sql`e.provider_model`
|
package/src/ai/validate.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Input } from "@k2b/nessi";
|
|
2
|
+
import type { AiModelRequest } from "../shared/ai-models";
|
|
2
3
|
import { readAiSettingsState, resolveAiModelFromState } from "./settings";
|
|
3
4
|
import type { AiModelPolicy, AiSettingsError, AiSettingsState } from "./types";
|
|
4
5
|
|
|
5
6
|
export const isAiSettingsError = (error: unknown): error is Error & { aiError: AiSettingsError } =>
|
|
6
|
-
error instanceof Error &&
|
|
7
|
+
error instanceof Error && "aiError" in error && typeof error.aiError === "object";
|
|
7
8
|
|
|
8
9
|
const inputIncludesFiles = (input: Input): boolean =>
|
|
9
10
|
Array.isArray(input) && input.some((part) => typeof part === "object" && part.type === "file");
|
|
@@ -13,27 +14,27 @@ export type ValidateAiTurnInput = {
|
|
|
13
14
|
hasImageAttachments?: boolean;
|
|
14
15
|
canInspectAttachedImages?: boolean;
|
|
15
16
|
modelPolicy?: AiModelPolicy;
|
|
16
|
-
|
|
17
|
+
model?: AiModelRequest;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export const validateAiTurnRequest = async (
|
|
20
21
|
input: ValidateAiTurnInput,
|
|
22
|
+
providedState?: AiSettingsState,
|
|
21
23
|
): Promise<{ settings: Extract<AiSettingsState, { ok: true }>; resolved: Awaited<ReturnType<typeof resolveAiModelFromState>> }> => {
|
|
22
|
-
const settings = await readAiSettingsState();
|
|
23
|
-
if (!settings.ok) throw Object.assign(new Error(settings.error.message), { aiError: settings.error });
|
|
24
|
+
const settings = providedState ?? (await readAiSettingsState());
|
|
24
25
|
if (!settings.enabled) {
|
|
25
26
|
throw Object.assign(new Error("AI is disabled."), {
|
|
26
27
|
aiError: { code: "ai_disabled", message: "AI is disabled." } satisfies AiSettingsError,
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const resolved = await resolveAiModelFromState(settings, input.modelPolicy ?? { kind: "platform-default" }, input.
|
|
31
|
+
const resolved = await resolveAiModelFromState(settings, input.modelPolicy ?? { kind: "platform-default" }, input.model);
|
|
31
32
|
if (inputIncludesFiles(input.input) && !resolved.profile.capabilities.includes("vision")) {
|
|
32
33
|
throw Object.assign(new Error(`AI model "${resolved.profile.id}" does not support image input.`), {
|
|
33
34
|
aiError: {
|
|
34
35
|
code: "model_policy_mismatch",
|
|
35
36
|
message: `AI model "${resolved.profile.label}" does not support image input.`,
|
|
36
|
-
fields: {
|
|
37
|
+
fields: { modelId: "Choose a model with vision support." },
|
|
37
38
|
} satisfies AiSettingsError,
|
|
38
39
|
});
|
|
39
40
|
}
|
|
@@ -44,7 +45,7 @@ export const validateAiTurnRequest = async (
|
|
|
44
45
|
aiError: {
|
|
45
46
|
code: "model_policy_mismatch",
|
|
46
47
|
message: `AI model "${resolved.profile.label}" needs Vision support or Tools with a configured Vision tool model.`,
|
|
47
|
-
fields: {
|
|
48
|
+
fields: { modelId: "Choose a Vision model, or configure the view_image fallback." },
|
|
48
49
|
} satisfies AiSettingsError,
|
|
49
50
|
});
|
|
50
51
|
}
|
package/src/ai/view-block.ts
CHANGED
|
@@ -41,7 +41,7 @@ export const CLOUD_AI_DETAILS_VIEWPORT_ITEMS = 12;
|
|
|
41
41
|
// the viewport at any width and its length counts no further.
|
|
42
42
|
export const CLOUD_AI_DETAILS_MAX_CHARS = 4096;
|
|
43
43
|
|
|
44
|
-
const referenceSchema = () => CloudResourceReferenceSchema.pick({ type: true, id: true, title: true });
|
|
44
|
+
const referenceSchema = () => CloudResourceReferenceSchema.pick({ type: true, id: true, title: true, context: true, status: true });
|
|
45
45
|
const ReferenceSchema = /* @__PURE__ */ referenceSchema();
|
|
46
46
|
export type CloudAiViewReference = z.output<typeof ReferenceSchema>;
|
|
47
47
|
const cellTypeSchema = () => z.enum(["text", "number", "date", "datetime", "ref"]);
|
|
@@ -371,7 +371,7 @@ const ExplorerViewSchema = ExplorerDataSchema.superRefine((value, ctx) => {
|
|
|
371
371
|
};
|
|
372
372
|
}
|
|
373
373
|
const domains = [
|
|
374
|
-
...("xAxis" in input && input.xAxis?.domain ? [input.xAxis.domain] : []),
|
|
374
|
+
...("xAxis" in input && input.kind !== "bar" && input.xAxis?.domain ? [input.xAxis.domain] : []),
|
|
375
375
|
...("yAxis" in input && input.yAxis?.domain ? [input.yAxis.domain] : []),
|
|
376
376
|
];
|
|
377
377
|
const stepping = domains.some(
|