@truefoundry/assistant-ui-runtime 0.1.17 → 0.1.18
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/CHANGELOG.md +13 -0
- package/README.md +4 -2
- package/dist/{chunk-S4SH2KT2.js → chunk-N6LJ3LR2.js} +160 -21
- package/dist/chunk-N6LJ3LR2.js.map +1 -0
- package/dist/index.d.ts +26 -5
- package/dist/index.js +147 -58
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +6 -2
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/{types-CctJojlz.d.ts → types-BRrQpoVy.d.ts} +1 -0
- package/package.json +2 -2
- package/src/hooks.ts +19 -19
- package/src/index.ts +5 -1
- package/src/plugins/truefoundry-agent-server-adapter/README.md +1 -1
- package/src/plugins/truefoundry-agent-server-adapter/cp.test.ts +3 -3
- package/src/plugins/truefoundry-agent-server-adapter/cp.ts +51 -10
- package/src/plugins/truefoundry-agent-server-adapter/modelReasoningEffort.test.ts +171 -0
- package/src/plugins/truefoundry-agent-server-adapter/modelReasoningEffort.ts +145 -0
- package/src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.test.ts +85 -4
- package/src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.ts +67 -17
- package/src/server/types.ts +1 -0
- package/src/truefoundryExtras.test.ts +161 -0
- package/src/truefoundryExtras.ts +136 -1
- package/dist/chunk-S4SH2KT2.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentSessionClient } from 'truefoundry-gateway-sdk/agents';
|
|
2
2
|
import { PrivateAgentSessionClient } from 'truefoundry-gateway-sdk/agents/private';
|
|
3
|
-
import { A as AgentSpec, N as CreateSessionRequest, Y as ListSessionsParams, S as Session, d as Turn, aE as TurnState, a as AgentChatServer, aM as UpdateSessionRequest, k as AgentSelectorEntry, G as ConnectorSelectorEntry, a8 as ModelSelectorEntry, az as SkillSelectorEntry, g as AgentBuilderServer } from '../../types-
|
|
3
|
+
import { A as AgentSpec, N as CreateSessionRequest, Y as ListSessionsParams, S as Session, d as Turn, aE as TurnState, a as AgentChatServer, aM as UpdateSessionRequest, k as AgentSelectorEntry, G as ConnectorSelectorEntry, a8 as ModelSelectorEntry, az as SkillSelectorEntry, g as AgentBuilderServer } from '../../types-BRrQpoVy.js';
|
|
4
4
|
import { TruefoundryGatewayApi } from 'truefoundry-gateway-sdk';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -116,7 +116,11 @@ declare function createTrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAge
|
|
|
116
116
|
*/
|
|
117
117
|
|
|
118
118
|
interface TfyModelSelectorEntry extends ModelSelectorEntry {
|
|
119
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* model_fqn for AgentSpec.model.name.
|
|
121
|
+
* Same value as {@link ModelSelectorEntry.name} / `id` — trueforge-ui writes
|
|
122
|
+
* `name` into the spec; keep this alias for hosts that still read `apiModel`.
|
|
123
|
+
*/
|
|
120
124
|
apiModel: string;
|
|
121
125
|
modelId: string;
|
|
122
126
|
providerAccount?: string;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { aH as TurnStreamingEvent, aO as DeltaEvents, T as TurnEvent } from '../types-
|
|
2
|
-
export { aP as ActionRequiredEvent, f as AgentBuilderCapabilitiesResponse, g as AgentBuilderServer, h as AgentCapabilityConfig, a as AgentChatServer, aQ as AgentInfo, i as AgentLibraryEntry, aR as AgentParent, j as AgentRuntimeConfig, k as AgentSelectorEntry, l as AgentSkill, A as AgentSpec, m as AgentUIServer, n as AgentUIServerPort, o as ApprovalDecision, p as AuthenticateConnectorRequest, C as CatalogServer, aS as ChunkDeltaToolCall, q as ConnectorAuth, r as ConnectorAuthApiKey, s as ConnectorAuthNone, t as ConnectorAuthOAuth, u as ConnectorAuthPublic, v as ConnectorAuthPublicApiKey, w as ConnectorAuthPublicNone, x as ConnectorAuthPublicOAuth, y as ConnectorAuthType, z as ConnectorAuthenticationResult, B as ConnectorBase, D as ConnectorCatalogEntry, E as ConnectorCatalogServer, F as ConnectorConfigBase, G as ConnectorSelectorEntry, H as ConnectorState, I as CreateConnectorRequest, J as CreateModelProviderRequest, K as CreateSandboxProviderRequest, L as CreateSandboxRequest, N as CreateSessionRequest, O as CreateSkillRequest, P as CreateSkillRequestBase, Q as DefinedSkill, R as GithubSkill, V as ImportGithubSkillRequest, W as ListResult, X as ListSessionsOrder, Y as ListSessionsParams, M as McpAuthRequiredEvent, aT as McpInitializeEvent, aU as McpServerAuthInfo, Z as McpServerMount, _ as Model, $ as ModelCatalogServer, a0 as ModelEntry, aV as ModelMessageContentPart, aW as ModelMessageDeltaEvent, a1 as ModelMessageEvent, a2 as ModelParams, a3 as ModelProperties, a4 as ModelProviderBase, a5 as ModelProviderCatalogEntry, a6 as ModelProviderConfigBase, a7 as ModelSelection, a8 as ModelSelectorEntry, a9 as PageParams, aa as PreviousTurnIdInput, ab as ProviderEntry, ac as ProviderType, ad as RegistrySkill, ae as SandboxBase, af as SandboxCatalogEntry, ag as SandboxCatalogServer, ah as SandboxConfig, ai as SandboxCreatedEvent, aj as SandboxProviderBase, ak as SandboxProviderCatalogEntry, al as SandboxProviderConfig, am as SandboxProviderListEntry, an as SandboxSnapshotSyncStatus, ao as SaveAgentRequest, ap as SaveAgentResult, aq as SearchAgentSelectorParams, ar as SearchAgentsParams, as as SelectRegistrySkillRequest, S as Session, at as SessionEventItem, au as SkillBase, av as SkillCatalogEntry, aw as SkillCatalogServer, ax as SkillConfigBase, ay as SkillMount, az as SkillSelectorEntry, b as ThreadCreatedEvent, aX as ThreadDoneEvent, aA as ToolApprovalRequiredEvent, aB as ToolBase, aC as ToolCall, aY as ToolCallFunction, aZ as ToolCallRef, a_ as ToolInfo, a$ as ToolResponseEvent, aD as ToolResponseRequiredEvent, d as Turn, b0 as TurnCreatedEvent, b1 as TurnDoneEvent, e as TurnInputItem, aE as TurnState, b2 as TurnStateCancelled, aF as TurnStateDone, b3 as TurnStateError, b4 as TurnStateRunning, aG as TurnStreamData, aI as UpdateConnectorRequest, aJ as UpdateModelProviderRequest, aK as UpdateSandboxProviderRequest, aL as UpdateSandboxRequest, aM as UpdateSessionRequest, aN as UserMessage, b5 as UserMessageContent, c as UserToolApprovalEvent, U as UserToolResponseEvent } from '../types-
|
|
1
|
+
import { aH as TurnStreamingEvent, aO as DeltaEvents, T as TurnEvent } from '../types-BRrQpoVy.js';
|
|
2
|
+
export { aP as ActionRequiredEvent, f as AgentBuilderCapabilitiesResponse, g as AgentBuilderServer, h as AgentCapabilityConfig, a as AgentChatServer, aQ as AgentInfo, i as AgentLibraryEntry, aR as AgentParent, j as AgentRuntimeConfig, k as AgentSelectorEntry, l as AgentSkill, A as AgentSpec, m as AgentUIServer, n as AgentUIServerPort, o as ApprovalDecision, p as AuthenticateConnectorRequest, C as CatalogServer, aS as ChunkDeltaToolCall, q as ConnectorAuth, r as ConnectorAuthApiKey, s as ConnectorAuthNone, t as ConnectorAuthOAuth, u as ConnectorAuthPublic, v as ConnectorAuthPublicApiKey, w as ConnectorAuthPublicNone, x as ConnectorAuthPublicOAuth, y as ConnectorAuthType, z as ConnectorAuthenticationResult, B as ConnectorBase, D as ConnectorCatalogEntry, E as ConnectorCatalogServer, F as ConnectorConfigBase, G as ConnectorSelectorEntry, H as ConnectorState, I as CreateConnectorRequest, J as CreateModelProviderRequest, K as CreateSandboxProviderRequest, L as CreateSandboxRequest, N as CreateSessionRequest, O as CreateSkillRequest, P as CreateSkillRequestBase, Q as DefinedSkill, R as GithubSkill, V as ImportGithubSkillRequest, W as ListResult, X as ListSessionsOrder, Y as ListSessionsParams, M as McpAuthRequiredEvent, aT as McpInitializeEvent, aU as McpServerAuthInfo, Z as McpServerMount, _ as Model, $ as ModelCatalogServer, a0 as ModelEntry, aV as ModelMessageContentPart, aW as ModelMessageDeltaEvent, a1 as ModelMessageEvent, a2 as ModelParams, a3 as ModelProperties, a4 as ModelProviderBase, a5 as ModelProviderCatalogEntry, a6 as ModelProviderConfigBase, a7 as ModelSelection, a8 as ModelSelectorEntry, a9 as PageParams, aa as PreviousTurnIdInput, ab as ProviderEntry, ac as ProviderType, ad as RegistrySkill, ae as SandboxBase, af as SandboxCatalogEntry, ag as SandboxCatalogServer, ah as SandboxConfig, ai as SandboxCreatedEvent, aj as SandboxProviderBase, ak as SandboxProviderCatalogEntry, al as SandboxProviderConfig, am as SandboxProviderListEntry, an as SandboxSnapshotSyncStatus, ao as SaveAgentRequest, ap as SaveAgentResult, aq as SearchAgentSelectorParams, ar as SearchAgentsParams, as as SelectRegistrySkillRequest, S as Session, at as SessionEventItem, au as SkillBase, av as SkillCatalogEntry, aw as SkillCatalogServer, ax as SkillConfigBase, ay as SkillMount, az as SkillSelectorEntry, b as ThreadCreatedEvent, aX as ThreadDoneEvent, aA as ToolApprovalRequiredEvent, aB as ToolBase, aC as ToolCall, aY as ToolCallFunction, aZ as ToolCallRef, a_ as ToolInfo, a$ as ToolResponseEvent, aD as ToolResponseRequiredEvent, d as Turn, b0 as TurnCreatedEvent, b1 as TurnDoneEvent, e as TurnInputItem, aE as TurnState, b2 as TurnStateCancelled, aF as TurnStateDone, b3 as TurnStateError, b4 as TurnStateRunning, aG as TurnStreamData, aI as UpdateConnectorRequest, aJ as UpdateModelProviderRequest, aK as UpdateSandboxProviderRequest, aL as UpdateSandboxRequest, aM as UpdateSessionRequest, aN as UserMessage, b5 as UserMessageContent, c as UserToolApprovalEvent, U as UserToolResponseEvent } from '../types-BRrQpoVy.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Local implementations of streaming delta helpers.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truefoundry/assistant-ui-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "TrueFoundry Gateway agent runtime adapter for assistant-ui",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -81,4 +81,4 @@
|
|
|
81
81
|
"typescript": "^5.9.3",
|
|
82
82
|
"vitest": "4.1.9"
|
|
83
83
|
}
|
|
84
|
-
}
|
|
84
|
+
}
|
package/src/hooks.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { useAui, useAuiState } from "@assistant-ui/store";
|
|
|
6
6
|
import type { TrueFoundryMessageCustomMetadata } from "./messageCustomMetadata.js";
|
|
7
7
|
import {
|
|
8
8
|
EMPTY_DRAFT_EXTRAS,
|
|
9
|
-
|
|
9
|
+
getTrueFoundryExtras,
|
|
10
|
+
useTrueFoundryRuntimeExtras,
|
|
10
11
|
type TrueFoundryDraftRuntimeExtras,
|
|
11
12
|
} from "./truefoundryExtras.js";
|
|
12
13
|
import type { RespondToToolApprovalOptions } from "./toolApproval.js";
|
|
@@ -14,7 +15,7 @@ import type { RespondToToolResponseOptions } from "./toolResponse.js";
|
|
|
14
15
|
|
|
15
16
|
/** Pending tool approvals plus a respond action. */
|
|
16
17
|
export const useTrueFoundryApprovals = () => {
|
|
17
|
-
const extras =
|
|
18
|
+
const extras = useTrueFoundryRuntimeExtras();
|
|
18
19
|
|
|
19
20
|
return useMemo(
|
|
20
21
|
() => ({
|
|
@@ -31,7 +32,7 @@ export const useTrueFoundryApprovals = () => {
|
|
|
31
32
|
|
|
32
33
|
/** Pending ask-user tool responses plus a respond action. */
|
|
33
34
|
export const useTrueFoundryToolResponses = () => {
|
|
34
|
-
const extras =
|
|
35
|
+
const extras = useTrueFoundryRuntimeExtras();
|
|
35
36
|
|
|
36
37
|
return useMemo(
|
|
37
38
|
() => ({
|
|
@@ -48,7 +49,7 @@ export const useTrueFoundryToolResponses = () => {
|
|
|
48
49
|
|
|
49
50
|
/** Pending MCP OAuth plus a resume action. */
|
|
50
51
|
export const useTrueFoundryMcpAuth = () => {
|
|
51
|
-
const extras =
|
|
52
|
+
const extras = useTrueFoundryRuntimeExtras();
|
|
52
53
|
|
|
53
54
|
return useMemo(
|
|
54
55
|
() => ({
|
|
@@ -67,25 +68,25 @@ export const useTrueFoundryMcpAuth = () => {
|
|
|
67
68
|
export const useTrueFoundryRespondToToolApproval = () => {
|
|
68
69
|
const aui = useAui();
|
|
69
70
|
return (response: RespondToToolApprovalOptions) =>
|
|
70
|
-
|
|
71
|
+
getTrueFoundryExtras(aui).respondToToolApproval(response);
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
/** Returns a function to respond to a pending tool response from any render context. */
|
|
74
75
|
export const useTrueFoundryRespondToToolResponse = () => {
|
|
75
76
|
const aui = useAui();
|
|
76
77
|
return (response: RespondToToolResponseOptions) =>
|
|
77
|
-
|
|
78
|
+
getTrueFoundryExtras(aui).respondToToolResponse(response);
|
|
78
79
|
};
|
|
79
80
|
|
|
80
81
|
/** Returns a function to resume after MCP OAuth from any render context. */
|
|
81
82
|
export const useTrueFoundryResumeMcpAuth = () => {
|
|
82
83
|
const aui = useAui();
|
|
83
|
-
return () =>
|
|
84
|
+
return () => getTrueFoundryExtras(aui).resumeMcpAuth();
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
/** Current sandboxId for this session, if a sandbox has been created. */
|
|
87
88
|
export const useTrueFoundrySandboxId = (): string | undefined =>
|
|
88
|
-
|
|
89
|
+
useTrueFoundryRuntimeExtras()?.sandboxId;
|
|
89
90
|
|
|
90
91
|
/** Turn that produced the message being rendered. Only defined inside a message scope. */
|
|
91
92
|
export const useTrueFoundryTurnId = (): string | undefined =>
|
|
@@ -108,25 +109,25 @@ export const useTrueFoundryDownloadSandboxFile = () => {
|
|
|
108
109
|
"Downloading a sandbox file requires a message scope to resolve its turn.",
|
|
109
110
|
);
|
|
110
111
|
}
|
|
111
|
-
return
|
|
112
|
+
return getTrueFoundryExtras(aui).downloadSandboxFile({ turnId, path });
|
|
112
113
|
};
|
|
113
114
|
};
|
|
114
115
|
|
|
115
116
|
/** Returns a function to cancel the current run from any render context. */
|
|
116
117
|
export const useTrueFoundryCancel = () => {
|
|
117
118
|
const aui = useAui();
|
|
118
|
-
return () =>
|
|
119
|
+
return () => getTrueFoundryExtras(aui).cancel();
|
|
119
120
|
};
|
|
120
121
|
|
|
121
122
|
/** Returns a function to reload (retry) the current session from any render context. */
|
|
122
123
|
export const useTrueFoundryReload = () => {
|
|
123
124
|
const aui = useAui();
|
|
124
|
-
return () =>
|
|
125
|
+
return () => getTrueFoundryExtras(aui).reload();
|
|
125
126
|
};
|
|
126
127
|
|
|
127
128
|
/** Older history pagination state plus a load-more action for scroll-up. */
|
|
128
129
|
export const useTrueFoundryHistoryPagination = () => {
|
|
129
|
-
const extras =
|
|
130
|
+
const extras = useTrueFoundryRuntimeExtras();
|
|
130
131
|
|
|
131
132
|
return useMemo(
|
|
132
133
|
() => ({
|
|
@@ -147,17 +148,17 @@ export const useTrueFoundryHistoryPagination = () => {
|
|
|
147
148
|
* arrive in this client. Falls back to `false` on runtimes that predate the flag.
|
|
148
149
|
*/
|
|
149
150
|
export const useTrueFoundryResumeUnavailable = () =>
|
|
150
|
-
|
|
151
|
+
useTrueFoundryRuntimeExtras()?.resumeUnavailable ?? false;
|
|
151
152
|
|
|
152
153
|
/** Returns a function to reset (re-submit) a user turn from any render context. */
|
|
153
154
|
export const useTrueFoundryResetFromTurn = () => {
|
|
154
155
|
const aui = useAui();
|
|
155
|
-
return (turnId: string) =>
|
|
156
|
+
return (turnId: string) => getTrueFoundryExtras(aui).resetFromTurn(turnId);
|
|
156
157
|
};
|
|
157
158
|
|
|
158
159
|
/** Current draft agent spec and sync state (draft mode only). */
|
|
159
160
|
export const useTrueFoundryAgentSpec = () => {
|
|
160
|
-
const extras =
|
|
161
|
+
const extras = useTrueFoundryRuntimeExtras()?.draft ?? null;
|
|
161
162
|
|
|
162
163
|
return useMemo(
|
|
163
164
|
() => ({ ...EMPTY_DRAFT_EXTRAS, ...extras }),
|
|
@@ -169,14 +170,14 @@ export const useTrueFoundryAgentSpec = () => {
|
|
|
169
170
|
export const useTrueFoundryUpdateAgentSpec = () => {
|
|
170
171
|
const aui = useAui();
|
|
171
172
|
return (update: Parameters<TrueFoundryDraftRuntimeExtras["updateAgentSpec"]>[0]) =>
|
|
172
|
-
|
|
173
|
+
getTrueFoundryExtras(aui).draft?.updateAgentSpec(update);
|
|
173
174
|
};
|
|
174
175
|
|
|
175
176
|
/** Flushes any pending draft-spec synchronization before a coordinated write. */
|
|
176
177
|
export const useTrueFoundryFlushAgentSpec = () => {
|
|
177
178
|
const aui = useAui();
|
|
178
179
|
return () =>
|
|
179
|
-
|
|
180
|
+
getTrueFoundryExtras(aui).draft?.flushAgentSpec() ?? Promise.resolve();
|
|
180
181
|
};
|
|
181
182
|
|
|
182
183
|
/** Adopts a spec already persisted by another server operation without syncing again. */
|
|
@@ -184,6 +185,5 @@ export const useTrueFoundryAdoptAgentSpec = () => {
|
|
|
184
185
|
const aui = useAui();
|
|
185
186
|
return (
|
|
186
187
|
request: Parameters<TrueFoundryDraftRuntimeExtras["adoptAgentSpec"]>[0],
|
|
187
|
-
) =>
|
|
188
|
+
) => getTrueFoundryExtras(aui).draft?.adoptAgentSpec(request);
|
|
188
189
|
};
|
|
189
|
-
|
package/src/index.ts
CHANGED
|
@@ -42,7 +42,11 @@ export type {
|
|
|
42
42
|
} from "./messageCustomMetadata.js";
|
|
43
43
|
export type { PendingApproval, PendingToolResponse } from "./collectPending.js";
|
|
44
44
|
export type { TrueFoundryRuntimeExtras } from "./truefoundryExtras.js";
|
|
45
|
-
export {
|
|
45
|
+
export {
|
|
46
|
+
trueFoundryExtras,
|
|
47
|
+
getTrueFoundryExtras,
|
|
48
|
+
tryGetTrueFoundryExtras,
|
|
49
|
+
} from "./truefoundryExtras.js";
|
|
46
50
|
export {
|
|
47
51
|
useTrueFoundryApprovals,
|
|
48
52
|
useTrueFoundryToolResponses,
|
|
@@ -89,7 +89,7 @@ Implemented against Control Plane HTTP (not the gateway SDK):
|
|
|
89
89
|
|
|
90
90
|
| Method | CP path |
|
|
91
91
|
| ------ | ------- |
|
|
92
|
-
| `getModels` | `GET /api/svc/v1/llm-gateway/model/enabled` |
|
|
92
|
+
| `getModels` | `GET /api/svc/v1/llm-gateway/model/enabled` (+ `GET /api/svc/v1/provider-accounts/providers` for `properties.reasoningEfforts`) |
|
|
93
93
|
| `getSkills` | `GET /api/ml/v1/agent-skills?include_empty_agent_skills=false` |
|
|
94
94
|
| `getMcp` | `GET /api/svc/v1/mcp-servers` |
|
|
95
95
|
| `searchAgents` | `GET /api/svc/v1/agents?type=truefoundry-agent&…` |
|
|
@@ -150,7 +150,7 @@ describe("normalizeEnabledModels", () => {
|
|
|
150
150
|
expect(rows).toEqual([
|
|
151
151
|
{
|
|
152
152
|
id: "openai-main/gpt-4.1",
|
|
153
|
-
name: "gpt-4.1",
|
|
153
|
+
name: "openai-main/gpt-4.1",
|
|
154
154
|
provider: { name: "openai" },
|
|
155
155
|
properties: {},
|
|
156
156
|
apiModel: "openai-main/gpt-4.1",
|
|
@@ -159,7 +159,7 @@ describe("normalizeEnabledModels", () => {
|
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
161
|
id: "anthropic-prod/claude-sonnet-4-6",
|
|
162
|
-
name: "claude-sonnet-4-6",
|
|
162
|
+
name: "anthropic-prod/claude-sonnet-4-6",
|
|
163
163
|
provider: { name: "anthropic" },
|
|
164
164
|
properties: {},
|
|
165
165
|
apiModel: "anthropic-prod/claude-sonnet-4-6",
|
|
@@ -186,7 +186,7 @@ describe("normalizeEnabledModels", () => {
|
|
|
186
186
|
expect(rows).toEqual([
|
|
187
187
|
{
|
|
188
188
|
id: "virtual-main/router",
|
|
189
|
-
name: "router",
|
|
189
|
+
name: "virtual-main/router",
|
|
190
190
|
provider: { name: "virtual-model" },
|
|
191
191
|
properties: {},
|
|
192
192
|
apiModel: "virtual-main/router",
|
|
@@ -13,6 +13,11 @@ import type {
|
|
|
13
13
|
SearchAgentSelectorParams,
|
|
14
14
|
SkillSelectorEntry,
|
|
15
15
|
} from "../../server/types.js";
|
|
16
|
+
import {
|
|
17
|
+
buildMetadataMap,
|
|
18
|
+
metadataKey,
|
|
19
|
+
reasoningEffortsForModel,
|
|
20
|
+
} from "./modelReasoningEffort.js";
|
|
16
21
|
import { normalizeAgentSpecForGateway } from "./normalizeAgentSpec.js";
|
|
17
22
|
import type { TfyAgentSpec } from "./types.js";
|
|
18
23
|
|
|
@@ -21,7 +26,11 @@ import type { TfyAgentSpec } from "./types.js";
|
|
|
21
26
|
// ---------------------------------------------------------------------------
|
|
22
27
|
|
|
23
28
|
export interface TfyModelSelectorEntry extends ModelSelectorEntry {
|
|
24
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* model_fqn for AgentSpec.model.name.
|
|
31
|
+
* Same value as {@link ModelSelectorEntry.name} / `id` — trueforge-ui writes
|
|
32
|
+
* `name` into the spec; keep this alias for hosts that still read `apiModel`.
|
|
33
|
+
*/
|
|
25
34
|
apiModel: string;
|
|
26
35
|
modelId: string;
|
|
27
36
|
providerAccount?: string;
|
|
@@ -158,15 +167,17 @@ function isChatModel(row: RawEnabledModel): boolean {
|
|
|
158
167
|
|
|
159
168
|
function toModelEntry(row: RawEnabledModel): TfyModelSelectorEntry | null {
|
|
160
169
|
if (!isChatModel(row)) return null;
|
|
170
|
+
// Gateway AgentSpec.model.name must be model_fqn (e.g. account/model-id).
|
|
171
|
+
// trueforge-ui DraftModelSelector writes ModelSelectorEntry.name into that
|
|
172
|
+
// field, so `name` is the FQN — not the CP short display label.
|
|
161
173
|
const apiModel = row.model_fqn ?? row.id;
|
|
162
|
-
|
|
163
|
-
if (apiModel == null || apiModel === "" || name == null || name === "") {
|
|
174
|
+
if (apiModel == null || apiModel === "") {
|
|
164
175
|
return null;
|
|
165
176
|
}
|
|
166
|
-
const modelId = row.model_id ?? name;
|
|
177
|
+
const modelId = row.model_id ?? row.name ?? apiModel;
|
|
167
178
|
return {
|
|
168
179
|
id: apiModel,
|
|
169
|
-
name,
|
|
180
|
+
name: apiModel,
|
|
170
181
|
provider: { name: row.provider ?? "unknown" },
|
|
171
182
|
properties: {},
|
|
172
183
|
apiModel,
|
|
@@ -219,14 +230,44 @@ export function normalizeEnabledModels(raw: unknown): TfyModelSelectorEntry[] {
|
|
|
219
230
|
return out;
|
|
220
231
|
}
|
|
221
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Attach `properties.reasoningEfforts` from provider-account metadata when the
|
|
235
|
+
* model supports thinking. Pure — used by {@link listEnabledModels} and tests.
|
|
236
|
+
*/
|
|
237
|
+
export function enrichModelsWithReasoningEfforts(
|
|
238
|
+
models: TfyModelSelectorEntry[],
|
|
239
|
+
providerMetadataRaw: unknown,
|
|
240
|
+
): TfyModelSelectorEntry[] {
|
|
241
|
+
const map = buildMetadataMap(providerMetadataRaw);
|
|
242
|
+
if (map.size === 0) return models;
|
|
243
|
+
return models.map((model) => {
|
|
244
|
+
const meta = map.get(metadataKey(model.provider.name, model.modelId));
|
|
245
|
+
const reasoningEfforts = reasoningEffortsForModel(
|
|
246
|
+
meta,
|
|
247
|
+
model.provider.name,
|
|
248
|
+
);
|
|
249
|
+
if (reasoningEfforts == null) return model;
|
|
250
|
+
return {
|
|
251
|
+
...model,
|
|
252
|
+
properties: { ...model.properties, reasoningEfforts },
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
222
257
|
export async function listEnabledModels(
|
|
223
258
|
opts: CpCredentials,
|
|
224
259
|
): Promise<TfyModelSelectorEntry[]> {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
"/api/svc/v1/llm-gateway/model/enabled",
|
|
228
|
-
|
|
229
|
-
|
|
260
|
+
// Parallel: providers is enrichment-only; soft-fail keeps models usable.
|
|
261
|
+
const [raw, providers] = await Promise.all([
|
|
262
|
+
cpFetch<unknown>(opts, "/api/svc/v1/llm-gateway/model/enabled"),
|
|
263
|
+
cpFetch<unknown>(
|
|
264
|
+
opts,
|
|
265
|
+
"/api/svc/v1/provider-accounts/providers",
|
|
266
|
+
).catch(() => null),
|
|
267
|
+
]);
|
|
268
|
+
const models = normalizeEnabledModels(raw);
|
|
269
|
+
if (providers == null) return models;
|
|
270
|
+
return enrichModelsWithReasoningEfforts(models, providers);
|
|
230
271
|
}
|
|
231
272
|
|
|
232
273
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
buildMetadataMap,
|
|
5
|
+
getEffortOptions,
|
|
6
|
+
reasoningEffortsForModel,
|
|
7
|
+
showsReasoningEffort,
|
|
8
|
+
type ModelMetadata,
|
|
9
|
+
} from "./modelReasoningEffort.js";
|
|
10
|
+
import {
|
|
11
|
+
enrichModelsWithReasoningEfforts,
|
|
12
|
+
type TfyModelSelectorEntry,
|
|
13
|
+
} from "./cp.js";
|
|
14
|
+
|
|
15
|
+
const thinkingMeta: ModelMetadata = {
|
|
16
|
+
thinking: true,
|
|
17
|
+
removeParams: ["temperature"],
|
|
18
|
+
params: [
|
|
19
|
+
{
|
|
20
|
+
key: "reasoning_effort",
|
|
21
|
+
type: "string",
|
|
22
|
+
defaultValue: "high",
|
|
23
|
+
supportedValues: ["low", "medium", "high", "max"],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const thinkingViaProviderDefaults: ModelMetadata = {
|
|
29
|
+
thinking: true,
|
|
30
|
+
params: [{ key: "max_tokens", maxValue: 128000 }],
|
|
31
|
+
defaultProviderParams: {
|
|
32
|
+
params: [
|
|
33
|
+
{
|
|
34
|
+
key: "reasoning_effort",
|
|
35
|
+
type: "string",
|
|
36
|
+
defaultValue: "high",
|
|
37
|
+
supportedValues: ["low", "medium", "high", "xhigh", "max"],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe("showsReasoningEffort", () => {
|
|
44
|
+
it("hides for openai exact slug even when thinking", () => {
|
|
45
|
+
expect(showsReasoningEffort(thinkingMeta, "openai")).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("shows for azure-openai / anthropic when thinking", () => {
|
|
49
|
+
expect(showsReasoningEffort(thinkingMeta, "azure-openai")).toBe(true);
|
|
50
|
+
expect(showsReasoningEffort(thinkingMeta, "anthropic")).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("hides when thinking is off or reasoning_effort is removed", () => {
|
|
54
|
+
expect(
|
|
55
|
+
showsReasoningEffort({ thinking: false }, "anthropic"),
|
|
56
|
+
).toBe(false);
|
|
57
|
+
expect(
|
|
58
|
+
showsReasoningEffort(
|
|
59
|
+
{ thinking: true, removeParams: ["reasoning_effort"] },
|
|
60
|
+
"anthropic",
|
|
61
|
+
),
|
|
62
|
+
).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("getEffortOptions / reasoningEffortsForModel", () => {
|
|
67
|
+
it("uses supportedValues from model params", () => {
|
|
68
|
+
expect(getEffortOptions(thinkingMeta)).toEqual([
|
|
69
|
+
"low",
|
|
70
|
+
"medium",
|
|
71
|
+
"high",
|
|
72
|
+
"max",
|
|
73
|
+
]);
|
|
74
|
+
expect(reasoningEffortsForModel(thinkingMeta, "anthropic")).toEqual([
|
|
75
|
+
"low",
|
|
76
|
+
"medium",
|
|
77
|
+
"high",
|
|
78
|
+
"max",
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("falls back to defaultProviderParams", () => {
|
|
83
|
+
expect(getEffortOptions(thinkingViaProviderDefaults)).toEqual([
|
|
84
|
+
"low",
|
|
85
|
+
"medium",
|
|
86
|
+
"high",
|
|
87
|
+
"xhigh",
|
|
88
|
+
"max",
|
|
89
|
+
]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("returns undefined when the picker should stay hidden", () => {
|
|
93
|
+
expect(reasoningEffortsForModel(thinkingMeta, "openai")).toBeUndefined();
|
|
94
|
+
expect(
|
|
95
|
+
reasoningEffortsForModel({ thinking: false }, "anthropic"),
|
|
96
|
+
).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("buildMetadataMap + enrichModelsWithReasoningEfforts", () => {
|
|
101
|
+
const providersPayload = [
|
|
102
|
+
{
|
|
103
|
+
type: "provider-account/anthropic",
|
|
104
|
+
integrations: [
|
|
105
|
+
{
|
|
106
|
+
metadata: {
|
|
107
|
+
"claude-sonnet-4-6": thinkingMeta,
|
|
108
|
+
"claude-haiku": { thinking: false },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: "provider-account/openai",
|
|
115
|
+
integrations: [
|
|
116
|
+
{
|
|
117
|
+
metadata: {
|
|
118
|
+
"o3-mini": thinkingMeta,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
it("indexes metadata by providerSlug/modelId", () => {
|
|
126
|
+
const map = buildMetadataMap(providersPayload);
|
|
127
|
+
expect(map.get("anthropic/claude-sonnet-4-6")).toEqual(thinkingMeta);
|
|
128
|
+
expect(map.get("openai/o3-mini")).toEqual(thinkingMeta);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("attaches properties.reasoningEfforts for thinking models only", () => {
|
|
132
|
+
const models: TfyModelSelectorEntry[] = [
|
|
133
|
+
{
|
|
134
|
+
id: "anthropic-prod/claude-sonnet-4-6",
|
|
135
|
+
name: "anthropic-prod/claude-sonnet-4-6",
|
|
136
|
+
provider: { name: "anthropic" },
|
|
137
|
+
properties: {},
|
|
138
|
+
apiModel: "anthropic-prod/claude-sonnet-4-6",
|
|
139
|
+
modelId: "claude-sonnet-4-6",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "anthropic-prod/claude-haiku",
|
|
143
|
+
name: "anthropic-prod/claude-haiku",
|
|
144
|
+
provider: { name: "anthropic" },
|
|
145
|
+
properties: {},
|
|
146
|
+
apiModel: "anthropic-prod/claude-haiku",
|
|
147
|
+
modelId: "claude-haiku",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "openai-main/o3-mini",
|
|
151
|
+
name: "openai-main/o3-mini",
|
|
152
|
+
provider: { name: "openai" },
|
|
153
|
+
properties: {},
|
|
154
|
+
apiModel: "openai-main/o3-mini",
|
|
155
|
+
modelId: "o3-mini",
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const next = enrichModelsWithReasoningEfforts(models, providersPayload);
|
|
160
|
+
|
|
161
|
+
expect(next[0]?.properties.reasoningEfforts).toEqual([
|
|
162
|
+
"low",
|
|
163
|
+
"medium",
|
|
164
|
+
"high",
|
|
165
|
+
"max",
|
|
166
|
+
]);
|
|
167
|
+
expect(next[1]?.properties.reasoningEfforts).toBeUndefined();
|
|
168
|
+
// exact openai slug hides even when thinking metadata exists
|
|
169
|
+
expect(next[2]?.properties.reasoningEfforts).toBeUndefined();
|
|
170
|
+
});
|
|
171
|
+
});
|