-
Notifications
You must be signed in to change notification settings - Fork 158
Feat/skills for client sdk #1868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,217 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||
| name: agentstack-client-sdk-essentials | ||||||||||||||||||||||||||||||||||||||||||||
| description: Basic implementation guidelines how to work with Agent Stack client SDK | ||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Custom GUI integration with Agent Stack requires dependency on both [A2A](https://a2a-protocol.org/latest/specification) as well as [Agent Stack](https://agentstack.beeai.dev/llms.txt). | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| You need to install both: | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||||||||||||||||||||
| pnpm add @a2a-js/sdk agentstack-sdk | ||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ## Get list of available agents | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| You need to know the agent UUID in advance. The easiest way to obtain it is via Agent Stack API client which is a wrapper around Agent Stack server API. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Agents are also called Providers. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||
| import { buildApiClient } from 'agentstack-sdk'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const agentstack = buildApiClient({ baseUrl: 'http://localhost:8334' }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const providers = await agentstack.listProviders({ | ||||||||||||||||||||||||||||||||||||||||||||
| query: {} | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (providers.ok) { | ||||||||||||||||||||||||||||||||||||||||||||
| providers.data.items.forEach(provider => { | ||||||||||||||||||||||||||||||||||||||||||||
| console.log(`The agent is named ${provider.agent_card.name} with id ${provider.id}`) | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ## Create Context and Token | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Before creating A2A client, you must create a context (session) in Agent Stack. The context ID is used to correlate all communication throughout the conversation. It is also used for A2A client to communicate with the agents. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||
| import { buildApiClient } from 'agentstack-sdk'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const agentstack = buildApiClient({ baseUrl: 'http://localhost:8334' }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const agentId = '548cd604-ce87-4ca4-b988-68357ca4cc40' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const context = await agentstack.createContext(agentId); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const { token } = await agentstack.createContextToken({ | ||||||||||||||||||||||||||||||||||||||||||||
| contextId: context.id, | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| globalPermissions: { llm: ['*'], a2a_proxy: ['*'] }, | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| contextPermissions: { | ||||||||||||||||||||||||||||||||||||||||||||
| files: ['*'], | ||||||||||||||||||||||||||||||||||||||||||||
| vector_stores: ['*'], | ||||||||||||||||||||||||||||||||||||||||||||
| context_data: ['*'], | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const contextId = context.id; | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code snippet has a few issues that will cause it to fail:
Here is a corrected version of this block for your reference: const contextResult = await agentstack.createContext({ provider_id: agentId });
if (!contextResult.ok) {
throw contextResult.error;
}
const context = contextResult.data;
const tokenResult = await agentstack.createContextToken({
contextId: context.id,
globalPermissions: { llm: ['*'], a2a_proxy: ['*'] },
contextPermissions: {
files: ['*'],
vector_stores: ['*'],
context_data: ['*'],
},
});
if (!tokenResult.ok) {
throw tokenResult.error;
}
const { token } = tokenResult.data;
const contextId = context.id; |
||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| The `contextId` from Agent Stack MUST be used in all A2A messages. Do not generate your own UUID. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ## Create A2A Client | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Agent Stack SDK extends [A2A - Agent to Agent](https://a2a-protocol.org/latest/specification) protocol. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| You need to create the client with a caveat that we need to provide fetch implemneation that packs in context token as authorization header. | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a typo in the word 'implementation'.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Agent Stack SDK exposes helper to build the fetch `createAuthenticatedFetch` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||
| ClientFactory, | ||||||||||||||||||||||||||||||||||||||||||||
| ClientFactoryOptions, | ||||||||||||||||||||||||||||||||||||||||||||
| DefaultAgentCardResolver, | ||||||||||||||||||||||||||||||||||||||||||||
| JsonRpcTransportFactory, | ||||||||||||||||||||||||||||||||||||||||||||
| } from '@a2a-js/sdk/client'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { createAuthenticatedFetch } from "agentstack-sdk"; | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The import statements in this file use a mix of single and double quotes. For consistency, it's best to stick to one style. The prevailing style in this file seems to be single quotes.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const agentstackUrl = 'http://localhost:8334' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const agentUrl = `${agentstackUrl}/api/v1/a2a/${agentId}/agent-card.json`; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const fetchImpl = createAuthenticatedFetch(token); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const factory = new ClientFactory( | ||||||||||||||||||||||||||||||||||||||||||||
| ClientFactoryOptions.createFrom(ClientFactoryOptions.default, { | ||||||||||||||||||||||||||||||||||||||||||||
| transports: [new JsonRpcTransportFactory({ fetchImpl })], | ||||||||||||||||||||||||||||||||||||||||||||
| cardResolver: new DefaultAgentCardResolver({ fetchImpl }), | ||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| const a2aClient = await factory.createFromUrl(agentUrl); | ||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ## Multi turn (chat) conversation with the agent | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Using A2A client to achieve multi turn (Chat) conversation with agent is relatively simple. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||
| import type { | ||||||||||||||||||||||||||||||||||||||||||||
| Message, | ||||||||||||||||||||||||||||||||||||||||||||
| TaskArtifactUpdateEvent, | ||||||||||||||||||||||||||||||||||||||||||||
| TaskStatusUpdateEvent, | ||||||||||||||||||||||||||||||||||||||||||||
| } from '@a2a-js/sdk'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const conversationHistory: Message[] = [] | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const clientPrompt = 'This is the initial message from user' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const clientMessage: Message = { | ||||||||||||||||||||||||||||||||||||||||||||
| messageId: crypto.randomUUID(), | ||||||||||||||||||||||||||||||||||||||||||||
| role: 'user', | ||||||||||||||||||||||||||||||||||||||||||||
| parts: [{ kind: 'text', text: clientPrompt }], | ||||||||||||||||||||||||||||||||||||||||||||
| kind: 'message', | ||||||||||||||||||||||||||||||||||||||||||||
| contextId, | ||||||||||||||||||||||||||||||||||||||||||||
| metadata: {} | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const messageStream = a2aClient.sendMessageStream({ message: clientMessage }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| function isStatusUpdate(event: unknown): event is TaskStatusUpdateEvent { | ||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||
| typeof event === 'object' && | ||||||||||||||||||||||||||||||||||||||||||||
| event !== null && | ||||||||||||||||||||||||||||||||||||||||||||
| 'kind' in event && | ||||||||||||||||||||||||||||||||||||||||||||
| (event as { kind: string }).kind === 'status-update' | ||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| let agentReply = '' | ||||||||||||||||||||||||||||||||||||||||||||
| for await (const event of messageStream) { | ||||||||||||||||||||||||||||||||||||||||||||
| if (isStatusUpdate(event) && event.status?.message?.parts) { | ||||||||||||||||||||||||||||||||||||||||||||
| for (const part of event.status.message.parts) { | ||||||||||||||||||||||||||||||||||||||||||||
| if (part.kind === 'text') { | ||||||||||||||||||||||||||||||||||||||||||||
| agentReply += part.text | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Client prompted ${clientPrompt} and agent responded with ${agentReply}) | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a typo in the
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ## Agent Stack Extensions | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Agent Stack extends A2A with extensions. They are advertised via Agent Card and supplied via Message metadata. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Agents declare demands via agent card extensions. Client fulfills demands using dependency injection: | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| 1. Fetch agent card from `/.well-known/agent-card.json` | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line states that the agent card should be fetched from |
||||||||||||||||||||||||||||||||||||||||||||
| 2. Figure out the demands in the Agent Card | ||||||||||||||||||||||||||||||||||||||||||||
| 3. Fulfill the demands in the client | ||||||||||||||||||||||||||||||||||||||||||||
| 4. Assemble and send the fulfillment via Message metadata. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ### Example of how fulfill LLM extension | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||
| buildApiClient, | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||||||||||||
| handleAgentCard, | ||||||||||||||||||||||||||||||||||||||||||||
| buildLLMExtensionFulfillmentResolver, | ||||||||||||||||||||||||||||||||||||||||||||
| type Fulfillments, | ||||||||||||||||||||||||||||||||||||||||||||
| } from 'agentstack-sdk'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const agentCard = await a2aClient.getAgentCard(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const { resolveMetadata, demands } = handleAgentCard(agentCard); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const fulfillments: Fulfillments = { | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| getContextToken: () => token, | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| llm: async (demand) => { | ||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||
| llm_fulfillments: Object.entries(demand.llm_demands).reduce((memo, [demandKey]) => { | ||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||
| ...memo, | ||||||||||||||||||||||||||||||||||||||||||||
| [demandKey]: { | ||||||||||||||||||||||||||||||||||||||||||||
| api_model: 'gpt5', | ||||||||||||||||||||||||||||||||||||||||||||
| api_base: 'http://openai-endpoint', | ||||||||||||||||||||||||||||||||||||||||||||
| api_key: 'API_KEY' | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| }, {}) | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+178
to
+188
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const metadata = await resolveMetadata(fulfillments); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const message: Message = { | ||||||||||||||||||||||||||||||||||||||||||||
| messageId: crypto.randomUUID(), | ||||||||||||||||||||||||||||||||||||||||||||
| role: 'user', | ||||||||||||||||||||||||||||||||||||||||||||
| parts: [{ kind: 'text', text: 'Message content' }], | ||||||||||||||||||||||||||||||||||||||||||||
| kind: 'message', | ||||||||||||||||||||||||||||||||||||||||||||
| contextId, | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| metadata, | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const stream = a2aClient.sendMessageStream({ message }); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| The Agent Stack provides LLM OpenAI compatible service that can be easily used and automatically resolved. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||
| import { buildLLMExtensionFulfillmentResolver } from 'agentstack-sdk'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (demands.llmDemands) { | ||||||||||||||||||||||||||||||||||||||||||||
| fulfillments.llm = buildLLMExtensionFulfillmentResolver(agentstack, token); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| name: agentstack-overview | ||
| description: Provides basic insight into what the main goal of Agent Stack is, how it's structured, and how it can be used. | ||
| --- | ||
|
|
||
| ## Agent Stack Goal | ||
|
|
||
| Agent Stack is a platform that provides infrastructure for developing and running AI agents. Agent builders can wrap their agent code with the Agent Stack SDK. The SDK creates a thin HTTP wrapper that exposes the [A2A - Agent to Agent](https://a2a-protocol.org/latest/specification) protocol, which is enhanced with custom [extensions](https://a2a-protocol.org/latest/topics/extensions/). | ||
|
|
||
| The exposed HTTP server is then registered with the AgentStack server. The Agent Stack UI then provides a GUI (or CLI) to run the agent. | ||
|
|
||
| ## Agent Stack Personas | ||
|
|
||
| ### Agent Builder | ||
|
|
||
| Typically a Python developer who either has an existing agent implemented in any framework (e.g., BeeAI, LangGraph, CrewAI) or is building an agent from scratch. This person wants to focus on building the agent, not on the interface, integration, or authentication. Agent Stack provides a quick and functional UI for their agents that they can use for local development, testing, or sharing with others. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The link for 'Agent Stack' points to a
llms.txtfile. This seems incorrect for a general reference to the Agent Stack project. It would be more helpful for users if this link pointed to the main project or documentation page.