Skip to content

Commit 16ae880

Browse files
authored
feat: extending TS sdk (#1609)
* feat: extending TS sdk Signed-off-by: Tomas Weiss <[email protected]> * fix: code review comments Signed-off-by: Tomas Weiss <[email protected]> * fix: remove GET from the callApi function, still keep the method for extensibility in the future Signed-off-by: Tomas Weiss <[email protected]> * chore: cleanup package.json Signed-off-by: Tomas Weiss <[email protected]> * chore: testing release pipeline Signed-off-by: Tomas Weiss <[email protected]> * chore: install mise Signed-off-by: Tomas Weiss <[email protected]> * fix: no git checks Signed-off-by: Tomas Weiss <[email protected]> * chore: setup release action Signed-off-by: Tomas Weiss <[email protected]> * fix: code review commnets Signed-off-by: Tomas Weiss <[email protected]> --------- Signed-off-by: Tomas Weiss <[email protected]>
1 parent 5468ff9 commit 16ae880

File tree

10 files changed

+230
-12
lines changed

10 files changed

+230
-12
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,7 @@ jobs:
151151
- uses: pypa/gh-action-pypi-publish@release/v1
152152
with:
153153
packages-dir: apps/agentstack-cli/dist
154+
155+
- name: Publish TS SDK
156+
working-directory: apps/agentstack-sdk-ts
157+
run: pnpm publish --access public --no-git-checks

apps/agentstack-sdk-ts/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"./package.json": "./package.json"
3333
},
3434
"files": [
35+
"src/**/*",
3536
"dist/**/*",
3637
"package.json"
3738
],
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright 2025 © BeeAI a Series of LF Projects, LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import type { AgentstackClient } from '../../../api/build-api-client';
7+
import type { ContextToken } from '../../../api/types';
8+
import { ModelCapability } from '../../../api/types';
9+
import type { LLMDemands, LLMFulfillments } from '../services/llm';
10+
11+
const DEFAULT_SCORE_CUTOFF = 0.4;
12+
13+
export const buildLLMExtensionFulfillmentResolver = (api: AgentstackClient, token: ContextToken) => {
14+
return async ({ llm_demands }: LLMDemands): Promise<LLMFulfillments> => {
15+
const allDemands = Object.keys(llm_demands);
16+
const fulfillmentPromises = allDemands.map(async (demandKey) => {
17+
const demand = llm_demands[demandKey];
18+
const resolvedModels = await api.matchProviders({
19+
suggestedModels: demand.suggested ?? [],
20+
capability: ModelCapability.Llm,
21+
scoreCutoff: DEFAULT_SCORE_CUTOFF,
22+
});
23+
24+
if (resolvedModels.items.length === 0) {
25+
throw new Error(`No models found for demand ${demandKey}. Demand details: ${JSON.stringify(demand)}`);
26+
}
27+
28+
return [
29+
demandKey,
30+
{
31+
identifier: 'llm_proxy',
32+
// {platform_url} is replaced by the server SDK to the platform URL
33+
api_base: '{platform_url}/api/v1/openai/',
34+
api_key: token.token,
35+
api_model: resolvedModels.items[0].model_id,
36+
},
37+
] as const;
38+
});
39+
40+
const fulfilledEntries = await Promise.all(fulfillmentPromises);
41+
return { llm_fulfillments: Object.fromEntries(fulfilledEntries) };
42+
};
43+
};

apps/agentstack-sdk-ts/src/client/a2a/extensions/handle-agent-card.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import type { AgentCapabilities } from '@a2a-js/sdk';
77

8-
import type { ContextToken } from '../../context/types';
8+
import type { ContextToken } from '../../api/types';
99
import type { EmbeddingDemands, EmbeddingFulfillments } from './services/embedding';
1010
import { embeddingExtension } from './services/embedding';
1111
import type { FormDemands, FormFulfillments } from './services/form';

apps/agentstack-sdk-ts/src/client/a2a/extensions/services/platform.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import type { ContextToken } from '../../../context/types';
6+
import type { ContextToken } from '../../../api/types';
77

88
const URI = 'https://a2a-extensions.agentstack.beeai.dev/services/platform_api/v1';
99

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright 2025 © BeeAI a Series of LF Projects, LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import type { AgentCapabilities, Message } from '@a2a-js/sdk';
7+
8+
import type { Fulfillments } from '../handle-agent-card';
9+
import { handleAgentCard } from '../handle-agent-card';
10+
11+
export const buildMessageBuilder =
12+
(agent: { capabilities: AgentCapabilities }) =>
13+
async (
14+
contextId: string,
15+
fulfillments: Fulfillments,
16+
originalMessage: Pick<Message, 'parts' | 'messageId'>,
17+
): Promise<Message> => {
18+
const { resolveMetadata } = handleAgentCard(agent);
19+
const metadata = await resolveMetadata(fulfillments);
20+
21+
return {
22+
...originalMessage,
23+
contextId,
24+
kind: 'message',
25+
role: 'user',
26+
metadata,
27+
} as const;
28+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright 2025 © BeeAI a Series of LF Projects, LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import type { z } from 'zod';
7+
8+
import type { ContextPermissionsGrant, GlobalPermissionsGrant, ModelCapability } from './types';
9+
import { contextSchema, contextTokenSchema, modelProviderMatchSchema } from './types';
10+
11+
interface MatchProvidersParams {
12+
suggestedModels: string[] | null;
13+
capability: ModelCapability;
14+
scoreCutoff: number;
15+
}
16+
17+
interface CreateContextTokenParams {
18+
contextId: string;
19+
globalPermissions: GlobalPermissionsGrant;
20+
contextPermissions: ContextPermissionsGrant;
21+
}
22+
23+
export const buildApiClient = ({ baseUrl }: { baseUrl: string } = { baseUrl: '' }) => {
24+
async function callApi<T>(method: 'POST', url: string, data: Record<string, unknown>, resultSchema: z.ZodSchema<T>) {
25+
const response = await fetch(`${baseUrl}${url}`, {
26+
method,
27+
headers: {
28+
'Content-Type': 'application/json',
29+
},
30+
body: JSON.stringify(data),
31+
});
32+
if (!response.ok) {
33+
throw new Error(`Failed to call Agent Stackk API - ${url}`);
34+
}
35+
36+
const json = await response.json();
37+
return resultSchema.parse(json);
38+
}
39+
40+
const createContext = async (providerId: string) => {
41+
const context = await callApi('POST', '/api/v1/contexts', { metadata: {}, provider_id: providerId }, contextSchema);
42+
43+
return context;
44+
};
45+
46+
const createContextToken = async ({ contextId, globalPermissions, contextPermissions }: CreateContextTokenParams) => {
47+
const token = await callApi(
48+
'POST',
49+
`/api/v1/contexts/${contextId}/token`,
50+
{
51+
grant_global_permissions: globalPermissions,
52+
grant_context_permissions: contextPermissions,
53+
},
54+
contextTokenSchema,
55+
);
56+
57+
return { token, contextId };
58+
};
59+
60+
const matchProviders = async ({ suggestedModels, capability, scoreCutoff }: MatchProvidersParams) => {
61+
return await callApi(
62+
'POST',
63+
'/api/v1/model_providers/match',
64+
{
65+
capability,
66+
score_cutoff: scoreCutoff,
67+
suggested_models: suggestedModels,
68+
},
69+
modelProviderMatchSchema,
70+
);
71+
};
72+
73+
return { createContextToken, createContext, matchProviders };
74+
};
75+
76+
export type AgentstackClient = ReturnType<typeof buildApiClient>;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Copyright 2025 © BeeAI a Series of LF Projects, LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { z } from 'zod';
7+
8+
export const contextSchema = z.object({
9+
id: z.string(),
10+
created_at: z.string(),
11+
updated_at: z.string(),
12+
last_active_at: z.string(),
13+
created_by: z.string(),
14+
provider_id: z.string().nullable(),
15+
metadata: z.record(z.string(), z.unknown()).nullable(),
16+
});
17+
18+
export const contextTokenSchema = z.object({
19+
token: z.string(),
20+
expires_at: z.string().nullable(),
21+
});
22+
export type ContextToken = z.infer<typeof contextTokenSchema>;
23+
24+
export enum ModelCapability {
25+
Llm = 'llm',
26+
Embedding = 'embedding',
27+
}
28+
29+
const paginatedResultSchema = z.object({
30+
items: z.array(z.unknown()),
31+
total_count: z.number(),
32+
has_more: z.boolean(),
33+
next_page_token: z.string().nullable(),
34+
});
35+
36+
export const modelProviderMatchSchema = paginatedResultSchema.extend({
37+
items: z.array(
38+
z.object({
39+
model_id: z.string(),
40+
score: z.number(),
41+
}),
42+
),
43+
});
44+
export type ModelProviderMatch = z.infer<typeof modelProviderMatchSchema>;
45+
46+
interface ResourceIdPermission {
47+
id: string;
48+
}
49+
50+
export interface ContextPermissionsGrant {
51+
files?: ('read' | 'write' | 'extract' | '*')[];
52+
vector_stores?: ('read' | 'write' | '*')[];
53+
context_data?: ('read' | 'write' | '*')[];
54+
}
55+
56+
export interface GlobalPermissionsGrant extends ContextPermissionsGrant {
57+
llm?: ('*' | ResourceIdPermission)[];
58+
embeddings?: ('*' | ResourceIdPermission)[];
59+
a2a_proxy?: '*'[];
60+
model_providers?: ('read' | 'write' | '*')[];
61+
variables?: ('read' | 'write' | '*')[];
62+
63+
providers?: ('read' | 'write' | '*')[];
64+
provider_variables?: ('read' | 'write' | '*')[];
65+
66+
contexts?: ('read' | 'write' | '*')[];
67+
mcp_providers?: ('read' | 'write' | '*')[];
68+
mcp_tools?: ('read' | '*')[];
69+
mcp_proxy?: '*'[];
70+
71+
connectors?: ('read' | 'write' | 'proxy' | '*')[];
72+
}

apps/agentstack-sdk-ts/src/client/context/types.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

apps/agentstack-sdk-ts/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
export * from './client/a2a/extensions/common/form';
7+
export * from './client/a2a/extensions/fulfillment-resolvers/build-llm-extension-fulfillment-resolver';
78
export { type Fulfillments, handleAgentCard } from './client/a2a/extensions/handle-agent-card';
89
export { handleInputRequired, type InputRequiredResponses } from './client/a2a/extensions/handle-input-required';
910
export {
@@ -26,4 +27,6 @@ export * from './client/a2a/extensions/ui/oauth';
2627
export * from './client/a2a/extensions/ui/settings';
2728
export * from './client/a2a/extensions/ui/trajectory';
2829
export * from './client/a2a/extensions/utils';
29-
export * from './client/context/types';
30+
export * from './client/a2a/extensions/utils/build-message-builder';
31+
export * from './client/api/build-api-client';
32+
export * from './client/api/types';

0 commit comments

Comments
 (0)