@ai11y/agent 0.0.1
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/.turbo/turbo-build.log +31 -0
- package/CHANGELOG.md +34 -0
- package/README.md +99 -0
- package/dist/agent.d.mts +13 -0
- package/dist/agent.d.mts.map +1 -0
- package/dist/agent.mjs +221 -0
- package/dist/agent.mjs.map +1 -0
- package/dist/fastify.d.mts +58 -0
- package/dist/fastify.d.mts.map +1 -0
- package/dist/fastify.mjs +82 -0
- package/dist/fastify.mjs.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/llm-provider.d.mts +12 -0
- package/dist/llm-provider.d.mts.map +1 -0
- package/dist/llm-provider.mjs +17 -0
- package/dist/llm-provider.mjs.map +1 -0
- package/dist/tool-registry.d.mts +52 -0
- package/dist/tool-registry.d.mts.map +1 -0
- package/dist/tool-registry.mjs +181 -0
- package/dist/tool-registry.mjs.map +1 -0
- package/dist/types.d.mts +14 -0
- package/dist/types.d.mts.map +1 -0
- package/package.json +40 -0
- package/src/agent.ts +416 -0
- package/src/fastify.ts +111 -0
- package/src/index.ts +11 -0
- package/src/llm-provider.ts +24 -0
- package/src/tool-registry.ts +235 -0
- package/src/types.ts +10 -0
- package/tsconfig.json +20 -0
- package/tsdown.config.ts +11 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
> @ai11y/agent@0.0.1 build /home/runner/work/ai11y/ai11y/packages/agent
|
|
3
|
+
> tsdown
|
|
4
|
+
|
|
5
|
+
[34mℹ[39m tsdown [2mv0.18.1[22m powered by rolldown [2mv1.0.0-beta.55[22m
|
|
6
|
+
[34mℹ[39m config file: [4m/home/runner/work/ai11y/ai11y/packages/agent/tsdown.config.ts[24m (unrun)
|
|
7
|
+
[34mℹ[39m entry: [34msrc/index.ts, src/fastify.ts[39m
|
|
8
|
+
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
|
+
[34mℹ[39m Build start
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mfastify.mjs[22m [2m 2.08 kB[22m [2m│ gzip: 0.94 kB[22m
|
|
11
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m 0.24 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22magent.mjs.map [2m20.59 kB[22m [2m│ gzip: 6.54 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22magent.mjs [2m12.04 kB[22m [2m│ gzip: 4.18 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22mtool-registry.mjs.map [2m 9.73 kB[22m [2m│ gzip: 2.75 kB[22m
|
|
15
|
+
[34mℹ[39m [2mdist/[22mtool-registry.mjs [2m 5.89 kB[22m [2m│ gzip: 1.91 kB[22m
|
|
16
|
+
[34mℹ[39m [2mdist/[22mfastify.mjs.map [2m 3.50 kB[22m [2m│ gzip: 1.43 kB[22m
|
|
17
|
+
[34mℹ[39m [2mdist/[22mllm-provider.mjs.map [2m 1.02 kB[22m [2m│ gzip: 0.56 kB[22m
|
|
18
|
+
[34mℹ[39m [2mdist/[22mllm-provider.mjs [2m 0.47 kB[22m [2m│ gzip: 0.31 kB[22m
|
|
19
|
+
[34mℹ[39m [2mdist/[22mtool-registry.d.mts.map [2m 0.36 kB[22m [2m│ gzip: 0.25 kB[22m
|
|
20
|
+
[34mℹ[39m [2mdist/[22mfastify.d.mts.map [2m 0.28 kB[22m [2m│ gzip: 0.20 kB[22m
|
|
21
|
+
[34mℹ[39m [2mdist/[22magent.d.mts.map [2m 0.23 kB[22m [2m│ gzip: 0.18 kB[22m
|
|
22
|
+
[34mℹ[39m [2mdist/[22mllm-provider.d.mts.map [2m 0.16 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
23
|
+
[34mℹ[39m [2mdist/[22mtypes.d.mts.map [2m 0.13 kB[22m [2m│ gzip: 0.12 kB[22m
|
|
24
|
+
[34mℹ[39m [2mdist/[22m[32m[1mfastify.d.mts[22m[39m [2m 1.57 kB[22m [2m│ gzip: 0.71 kB[22m
|
|
25
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 0.30 kB[22m [2m│ gzip: 0.16 kB[22m
|
|
26
|
+
[34mℹ[39m [2mdist/[22m[32mtool-registry.d.mts[39m [2m 1.35 kB[22m [2m│ gzip: 0.61 kB[22m
|
|
27
|
+
[34mℹ[39m [2mdist/[22m[32magent.d.mts[39m [2m 0.42 kB[22m [2m│ gzip: 0.26 kB[22m
|
|
28
|
+
[34mℹ[39m [2mdist/[22m[32mllm-provider.d.mts[39m [2m 0.36 kB[22m [2m│ gzip: 0.25 kB[22m
|
|
29
|
+
[34mℹ[39m [2mdist/[22m[32mtypes.d.mts[39m [2m 0.26 kB[22m [2m│ gzip: 0.20 kB[22m
|
|
30
|
+
[34mℹ[39m 20 files, total: 60.98 kB
|
|
31
|
+
[32m✔[39m Build complete in [32m3596ms[39m
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @ai11y/agent
|
|
2
|
+
|
|
3
|
+
## 0.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#10](https://github.com/maerzhase/ai11y/pull/10)
|
|
8
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)
|
|
9
|
+
Thanks [@maerzhase](https://github.com/maerzhase)! - Docs and demo
|
|
10
|
+
improvements: naming (ai11y ≠ a11y), Describe/Plan/Act boundaries, UI context
|
|
11
|
+
payload viewer, annotation guidance, Why not ARIA, Security & privacy,
|
|
12
|
+
multi-step demo
|
|
13
|
+
|
|
14
|
+
- [#10](https://github.com/maerzhase/ai11y/pull/10)
|
|
15
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)
|
|
16
|
+
Thanks [@maerzhase](https://github.com/maerzhase)! - Add custom state
|
|
17
|
+
management and privacy protection features
|
|
18
|
+
- **Custom State API**: `setState()` now merges with existing state (like
|
|
19
|
+
React's setState), allowing multiple components to independently manage
|
|
20
|
+
state keys. Added `clearState()` helper for resetting state.
|
|
21
|
+
- **Privacy Protection**: Automatically redact sensitive input values
|
|
22
|
+
(passwords, hidden fields, credit card fields) from UI context. Values are
|
|
23
|
+
replaced with `[REDACTED]` to prevent exposure to AI agents.
|
|
24
|
+
- **Sensitive Marker Support**: Added `data-ai-sensitive` attribute and
|
|
25
|
+
`sensitive` prop to Marker component for explicitly marking sensitive
|
|
26
|
+
fields.
|
|
27
|
+
- **Agent Improvements**: Enhanced agent prompts with security rules to
|
|
28
|
+
prevent filling password fields and improved pronoun resolution for better
|
|
29
|
+
context tracking in conversations.
|
|
30
|
+
|
|
31
|
+
- Updated dependencies
|
|
32
|
+
[[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d),
|
|
33
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)]:
|
|
34
|
+
- @ai11y/core@0.0.1
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @ai11y/agent
|
|
2
|
+
|
|
3
|
+
Runs the **plan** step for ai11y on the server (LLM + tools). Securely handles
|
|
4
|
+
LLM API calls and provides extensible tool support.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm add @ai11y/agent
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### Standalone
|
|
15
|
+
|
|
16
|
+
Use `runAgent` with a config and tool registry; wire it to your own HTTP (or
|
|
17
|
+
other) transport:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import type { AgentRequest } from "@ai11y/core";
|
|
21
|
+
import { runAgent, createDefaultToolRegistry } from "@ai11y/agent";
|
|
22
|
+
import type { ServerConfig } from "@ai11y/agent";
|
|
23
|
+
|
|
24
|
+
const config: ServerConfig = {
|
|
25
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
26
|
+
model: "gpt-4o-mini",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const toolRegistry = createDefaultToolRegistry();
|
|
30
|
+
|
|
31
|
+
async function handleRequest(request: AgentRequest) {
|
|
32
|
+
const response = await runAgent(request, config, toolRegistry);
|
|
33
|
+
return response;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### With Fastify
|
|
38
|
+
|
|
39
|
+
Register the plugin to expose the agent endpoint:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import Fastify from "fastify";
|
|
43
|
+
import { ai11yPlugin } from "@ai11y/agent/fastify";
|
|
44
|
+
|
|
45
|
+
const fastify = Fastify();
|
|
46
|
+
|
|
47
|
+
await fastify.register(ai11yPlugin, {
|
|
48
|
+
config: {
|
|
49
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
50
|
+
model: "gpt-4o-mini",
|
|
51
|
+
baseURL: "https://api.openai.com/v1",
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await fastify.listen({ port: 3000 });
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The plugin registers `POST /ai11y/agent` and `GET /ai11y/health`. See
|
|
59
|
+
`apps/server/` for a full example.
|
|
60
|
+
|
|
61
|
+
## Extending with Custom Tools
|
|
62
|
+
|
|
63
|
+
You can extend the agent with custom tools using the `ToolRegistry`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import type { Ai11yContext, ToolDefinition, ToolExecutor } from "@ai11y/core";
|
|
67
|
+
import { ai11yPlugin, createToolRegistry } from "@ai11y/agent/fastify";
|
|
68
|
+
|
|
69
|
+
const registry = createToolRegistry();
|
|
70
|
+
|
|
71
|
+
registry.register(
|
|
72
|
+
{
|
|
73
|
+
name: "send_email",
|
|
74
|
+
description: "Send an email notification",
|
|
75
|
+
parameters: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
to: { type: "string", description: "Recipient email address" },
|
|
79
|
+
subject: { type: "string", description: "Email subject" },
|
|
80
|
+
body: { type: "string", description: "Email body" },
|
|
81
|
+
},
|
|
82
|
+
required: ["to", "subject", "body"],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
async (args, context) => {
|
|
86
|
+
console.log("Sending email:", args);
|
|
87
|
+
return { success: true, messageId: "123" };
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
await fastify.register(ai11yPlugin, {
|
|
92
|
+
config: {
|
|
93
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
94
|
+
},
|
|
95
|
+
toolRegistry: registry,
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For API details and types, see the generated docs.
|
package/dist/agent.d.mts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ToolRegistry } from "./tool-registry.mjs";
|
|
2
|
+
import { ServerConfig } from "./types.mjs";
|
|
3
|
+
import { AgentRequest, AgentResponse } from "@ai11y/core";
|
|
4
|
+
|
|
5
|
+
//#region src/agent.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Run the LLM agent on the server
|
|
9
|
+
*/
|
|
10
|
+
declare function runAgent(request: AgentRequest, config: ServerConfig, toolRegistry?: ToolRegistry): Promise<AgentResponse>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { runAgent };
|
|
13
|
+
//# sourceMappingURL=agent.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.mts","names":[],"sources":["../src/agent.ts"],"sourcesContent":[],"mappings":";;;;;;;;AA+NA;AACU,iBADY,QAAA,CACZ,OAAA,EAAA,YAAA,EAAA,MAAA,EACD,YADC,EAAA,YAAA,CAAA,EAEK,YAFL,CAAA,EAGP,OAHO,CAGC,aAHD,CAAA"}
|
package/dist/agent.mjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createLLM } from "./llm-provider.mjs";
|
|
2
|
+
import { createDefaultToolRegistry } from "./tool-registry.mjs";
|
|
3
|
+
import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/agent.ts
|
|
7
|
+
/**
|
|
8
|
+
* Format context for the LLM prompt
|
|
9
|
+
*/
|
|
10
|
+
function formatContextForPrompt(context) {
|
|
11
|
+
const parts = [];
|
|
12
|
+
if (context.route) parts.push(`Current route: ${context.route}`);
|
|
13
|
+
if (context.error) {
|
|
14
|
+
const error = context.error.error;
|
|
15
|
+
parts.push(`\n! Last error: ${error.message}${context.error.meta?.markerId ? ` (related to marker: ${context.error.meta.markerId})` : ""}`);
|
|
16
|
+
parts.push("The user may want to retry the failed action. Look for markers related to the error.");
|
|
17
|
+
}
|
|
18
|
+
if (context.state && Object.keys(context.state).length > 0) parts.push(`\nApplication state: ${JSON.stringify(context.state)}`);
|
|
19
|
+
if (context.markers.length > 0) {
|
|
20
|
+
parts.push("\nAvailable UI elements (markers):");
|
|
21
|
+
for (const marker of context.markers) {
|
|
22
|
+
const inViewStatus = context.inViewMarkerIds?.includes(marker.id) ?? false ? " [IN VIEW]" : "";
|
|
23
|
+
let markerLine = ` - ${marker.label} (ID: ${marker.id}, Type: ${marker.elementType})${inViewStatus}: ${marker.intent}`;
|
|
24
|
+
if (marker.value !== void 0) markerLine += `\n Current value: "${marker.value}"`;
|
|
25
|
+
if (marker.options !== void 0) {
|
|
26
|
+
const optionsList = marker.options.map((opt) => `${opt.label} (${opt.value})`).join(", ");
|
|
27
|
+
markerLine += `\n Available options: ${optionsList}`;
|
|
28
|
+
}
|
|
29
|
+
if (marker.selectedOptions !== void 0 && marker.selectedOptions.length > 0) markerLine += `\n Selected: ${marker.selectedOptions.join(", ")}`;
|
|
30
|
+
parts.push(markerLine);
|
|
31
|
+
}
|
|
32
|
+
} else parts.push("\nNo UI elements are currently available.");
|
|
33
|
+
if (context.inViewMarkerIds && context.inViewMarkerIds.length > 0) parts.push(`\nVisible markers: ${context.inViewMarkerIds.join(", ")}`);
|
|
34
|
+
return parts.join("\n");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert tool registry to LangChain tools
|
|
38
|
+
*/
|
|
39
|
+
function createLangChainTools(toolRegistry, context) {
|
|
40
|
+
const tools = [];
|
|
41
|
+
const toolDefinitions = toolRegistry.getToolDefinitions();
|
|
42
|
+
for (const toolDef of toolDefinitions) {
|
|
43
|
+
const def = toolDef.function;
|
|
44
|
+
const zodSchema = {};
|
|
45
|
+
for (const [key, param] of Object.entries(def.parameters.properties)) zodSchema[key] = (param.type === "string" ? z.string() : param.type === "number" ? z.number() : param.type === "boolean" ? z.boolean() : z.unknown()).describe(param.description);
|
|
46
|
+
const schema = z.object(zodSchema);
|
|
47
|
+
const tool = new DynamicStructuredTool({
|
|
48
|
+
name: def.name,
|
|
49
|
+
description: def.description,
|
|
50
|
+
schema,
|
|
51
|
+
func: async (args) => {
|
|
52
|
+
const result = await toolRegistry.executeToolCall(def.name, args, context);
|
|
53
|
+
return JSON.stringify(result);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
tools.push(tool);
|
|
57
|
+
}
|
|
58
|
+
return tools;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if user input matches any marker and return matching marker info
|
|
62
|
+
* Also checks if user is referring to a UI element (button, link, etc.) vs just a route
|
|
63
|
+
*/
|
|
64
|
+
function findMatchingMarker(input, markers) {
|
|
65
|
+
const lowerInput = input.toLowerCase().trim();
|
|
66
|
+
if (!(lowerInput.includes("go to") || lowerInput.includes("navigate to") || lowerInput.includes("open") || lowerInput.includes("take me to"))) return null;
|
|
67
|
+
const isElementReference = [
|
|
68
|
+
"button",
|
|
69
|
+
"link",
|
|
70
|
+
"nav button",
|
|
71
|
+
"navigation button",
|
|
72
|
+
"nav link",
|
|
73
|
+
"navigation link",
|
|
74
|
+
"element",
|
|
75
|
+
"item",
|
|
76
|
+
"tab"
|
|
77
|
+
].some((keyword) => lowerInput.includes(keyword));
|
|
78
|
+
const searchText = lowerInput.replace(/go to|navigate to|open|take me to/g, "").trim();
|
|
79
|
+
const matchingMarker = markers.find((m) => {
|
|
80
|
+
return `${m.label} ${m.intent}`.toLowerCase().includes(searchText) || lowerInput.includes(m.label.toLowerCase()) || lowerInput.includes(m.intent.toLowerCase());
|
|
81
|
+
});
|
|
82
|
+
return matchingMarker ? {
|
|
83
|
+
marker: matchingMarker,
|
|
84
|
+
searchText,
|
|
85
|
+
isElementReference
|
|
86
|
+
} : null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Type guard to check if response has tool calls
|
|
90
|
+
*/
|
|
91
|
+
function hasToolCalls(response) {
|
|
92
|
+
const kwargs = response.additional_kwargs;
|
|
93
|
+
return typeof response === "object" && response !== null && ("tool_calls" in response || "additional_kwargs" in response && typeof kwargs === "object" && kwargs !== null && "tool_calls" in kwargs);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Run the LLM agent on the server
|
|
97
|
+
*/
|
|
98
|
+
async function runAgent(request, config, toolRegistry = createDefaultToolRegistry()) {
|
|
99
|
+
const llm = await createLLM(config);
|
|
100
|
+
const markerMatch = findMatchingMarker(request.input, request.context.markers);
|
|
101
|
+
const contextPrompt = formatContextForPrompt(request.context);
|
|
102
|
+
const langchainTools = createLangChainTools(toolRegistry, request.context);
|
|
103
|
+
const llmWithTools = langchainTools.length > 0 && typeof llm.bindTools === "function" ? llm.bindTools(langchainTools) : llm;
|
|
104
|
+
let recentMarkerContext = "";
|
|
105
|
+
if (request.messages && request.messages.length > 0) {
|
|
106
|
+
const lastFewMessages = request.messages.slice(-4);
|
|
107
|
+
for (const msg of lastFewMessages) for (const marker of request.context.markers) if (msg.content.toLowerCase().includes(marker.label.toLowerCase()) || msg.content.toLowerCase().includes(marker.id.toLowerCase())) {
|
|
108
|
+
recentMarkerContext += `\nRecently discussed: ${marker.label} (${marker.id}) - ${marker.intent}`;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let markerMatchGuidance = "";
|
|
113
|
+
if (markerMatch) {
|
|
114
|
+
const marker = markerMatch.marker;
|
|
115
|
+
const isInView = request.context.inViewMarkerIds?.includes(marker.id) ?? false;
|
|
116
|
+
const isLink = marker.elementType === "a";
|
|
117
|
+
if (isLink && isInView) markerMatchGuidance = `\n\n⚠️ Match found: "${marker.label}" (${marker.id}) is a visible link. Use 'click' tool.`;
|
|
118
|
+
else if (isLink && !isInView) markerMatchGuidance = `\n\n🚨 Match found: "${marker.label}" (${marker.id}) is a link NOT in view. You MUST call BOTH 'scroll' and 'click' (in that order)—two tool calls. Do not call only scroll.`;
|
|
119
|
+
else if (!isInView) markerMatchGuidance = `\n\n🚨 Match found: "${marker.label}" (${marker.id}) is NOT in view. You MUST call BOTH 'scroll' and 'click' (or the appropriate action) in that order—two tool calls. Do not call only scroll.`;
|
|
120
|
+
else markerMatchGuidance = `\n\nMatch found: "${marker.label}" (${marker.id}) is an element. Use 'scroll' tool.`;
|
|
121
|
+
}
|
|
122
|
+
const conversationMessages = [{
|
|
123
|
+
role: "system",
|
|
124
|
+
content: `You are a helpful AI agent embedded in a web application. Help users navigate, interact with UI elements, and resolve errors.
|
|
125
|
+
|
|
126
|
+
${contextPrompt}${recentMarkerContext}${markerMatchGuidance}
|
|
127
|
+
|
|
128
|
+
Reading marker values:
|
|
129
|
+
- You can read current values from markers in the context above
|
|
130
|
+
- For input/textarea elements: The "Current value" field shows what's currently entered (password fields show "[REDACTED]" for privacy)
|
|
131
|
+
- For select elements: The "Available options" shows all choices, and "Selected" shows what's currently selected
|
|
132
|
+
- When users ask "what's in [field]" or "what's the value of [marker]", read the value from the context and respond with it
|
|
133
|
+
- You don't need a tool to read values - they're already in the context provided above
|
|
134
|
+
|
|
135
|
+
Security rules:
|
|
136
|
+
- NEVER fill password fields - if a user asks to fill a password, politely explain that sending passwords is a security risk and they should enter it manually
|
|
137
|
+
- Password field values are redacted as "[REDACTED]" in the context for privacy protection
|
|
138
|
+
|
|
139
|
+
Pronoun resolution:
|
|
140
|
+
- When the user says "it", "that", "this", they're referring to the most recently discussed marker
|
|
141
|
+
- Look at the recent conversation history (shown above as "Recently discussed") to identify which specific marker was discussed
|
|
142
|
+
- Prefer specific interactive elements (buttons, inputs, links) over parent sections (those with "section" or "slide" in the label/id) when resolving pronouns
|
|
143
|
+
- Example: If user asked about "password input" and then says "highlight it", use the password input marker (e.g. fill_demo_password), NOT the parent section marker (e.g. slide_fill_input)
|
|
144
|
+
|
|
145
|
+
Navigation rules:
|
|
146
|
+
- "navigate to [element]" = scroll to that element (use 'scroll' tool)
|
|
147
|
+
- "navigate to [route]" = route navigation (use 'navigate' tool with route path)
|
|
148
|
+
- If marker matches and is in inViewMarkerIds + elementType='a' → use 'click'
|
|
149
|
+
- If no marker matches → use 'navigate' with route path
|
|
150
|
+
- For affirmative responses after discussing a marker, interact with that marker using the appropriate tool.
|
|
151
|
+
|
|
152
|
+
Scroll-then-act rule (CRITICAL):
|
|
153
|
+
- When the user wants to INTERACT with an element (click, press, increment, submit, fill, etc.) and the target marker is NOT in inViewMarkerIds: you MUST emit TWO tool calls in order—(1) 'scroll' to that marker, (2) the action ('click', 'fillInput', etc.). Emitting only 'scroll' is WRONG; the user asked for an action, so you must call both scroll and the action.
|
|
154
|
+
- Prefer the specific interactive element for the action (e.g. the button click_demo_increment for "increment counter"), not a parent section (e.g. slide_click). Emit one scroll to the exact target element, then the action.
|
|
155
|
+
- If the user only wants to see or navigate to an element (no click/fill), use a single 'scroll' to that element.
|
|
156
|
+
|
|
157
|
+
Relative scrolling rules (for "scroll to next" or "scroll to previous"):
|
|
158
|
+
- Markers are listed in document order (top to bottom) in the markers array
|
|
159
|
+
- CRITICAL: Always skip markers that are in inViewMarkerIds - only scroll to markers NOT currently in view
|
|
160
|
+
- For "scroll to next": Find the first marker in the markers array that comes after any currently visible markers and is NOT in inViewMarkerIds
|
|
161
|
+
- For "scroll to previous": Find the first marker in the markers array that comes before any currently visible markers and is NOT in inViewMarkerIds
|
|
162
|
+
- This prevents getting stuck on sections already in view
|
|
163
|
+
|
|
164
|
+
Multiple actions rules:
|
|
165
|
+
- When user says "all" (e.g., "highlight all badges", "click all buttons"), generate MULTIPLE tool calls - one for each matching marker
|
|
166
|
+
- When user specifies a count (e.g., "click 10 times", "increment counter 5 times"), generate MULTIPLE tool calls - repeat the action the specified number of times
|
|
167
|
+
- CRITICAL: You can and should call the same tool multiple times in a single response when the user requests multiple actions
|
|
168
|
+
- For "highlight all [type]" or "click all [type]": Find all markers matching the type and generate one tool call per marker
|
|
169
|
+
- For "[action] [count] times": Generate [count] identical tool calls`
|
|
170
|
+
}];
|
|
171
|
+
if (request.messages && request.messages.length > 0) {
|
|
172
|
+
const recentMessages = request.messages.slice(-10);
|
|
173
|
+
for (const msg of recentMessages) conversationMessages.push({
|
|
174
|
+
role: msg.role,
|
|
175
|
+
content: msg.content
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
conversationMessages.push({
|
|
179
|
+
role: "user",
|
|
180
|
+
content: request.input
|
|
181
|
+
});
|
|
182
|
+
const response = await llmWithTools.invoke(conversationMessages);
|
|
183
|
+
const instructions = [];
|
|
184
|
+
let reply = "";
|
|
185
|
+
if (response.content) if (typeof response.content === "string") reply = response.content;
|
|
186
|
+
else if (Array.isArray(response.content)) reply = response.content.map((c) => {
|
|
187
|
+
if (typeof c === "string") return c;
|
|
188
|
+
if (c && typeof c === "object" && "text" in c) return c.text;
|
|
189
|
+
return "";
|
|
190
|
+
}).join("");
|
|
191
|
+
else reply = String(response.content);
|
|
192
|
+
const toolCallObjects = hasToolCalls(response) ? response.tool_calls || response.additional_kwargs?.tool_calls || [] : [];
|
|
193
|
+
for (const toolCall of toolCallObjects) {
|
|
194
|
+
const toolName = toolCall.name || toolCall.function?.name;
|
|
195
|
+
const toolArgs = toolCall.args || (toolCall.function?.arguments ? typeof toolCall.function.arguments === "string" ? JSON.parse(toolCall.function.arguments) : toolCall.function.arguments : {});
|
|
196
|
+
if (toolName) {
|
|
197
|
+
const converted = toolRegistry.convertToolCall({
|
|
198
|
+
type: "function",
|
|
199
|
+
function: {
|
|
200
|
+
name: toolName,
|
|
201
|
+
arguments: JSON.stringify(toolArgs)
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
if (converted) instructions.push(converted);
|
|
205
|
+
else try {
|
|
206
|
+
const result = await toolRegistry.executeToolCall(toolName, toolArgs, request.context);
|
|
207
|
+
if (result && typeof result === "object" && "action" in result) instructions.push(result);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(`Error executing tool ${toolName}:`, error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
reply: reply || "I'm here to help!",
|
|
215
|
+
instructions: instructions.length > 0 ? instructions : void 0
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//#endregion
|
|
220
|
+
export { runAgent };
|
|
221
|
+
//# sourceMappingURL=agent.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.mjs","names":["parts: string[]","tools: DynamicStructuredTool[]","zodSchema: Record<string, z.ZodTypeAny>","conversationMessages: Array<{ role: string; content: string }>","instructions: Instruction[]","toolCallObjects: Array<{\n\t\tname?: string;\n\t\targs?: Record<string, unknown>;\n\t\tfunction?: {\n\t\t\tname?: string;\n\t\t\targuments?: string | Record<string, unknown>;\n\t\t};\n\t}>"],"sources":["../src/agent.ts"],"sourcesContent":["import type { AgentRequest, AgentResponse, Instruction } from \"@ai11y/core\";\nimport { DynamicStructuredTool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { createLLM } from \"./llm-provider.js\";\nimport {\n\tcreateDefaultToolRegistry,\n\ttype ToolRegistry,\n} from \"./tool-registry.js\";\nimport type { ServerConfig } from \"./types.js\";\n\n/**\n * Format context for the LLM prompt\n */\nfunction formatContextForPrompt(context: AgentRequest[\"context\"]): string {\n\tconst parts: string[] = [];\n\n\tif (context.route) {\n\t\tparts.push(`Current route: ${context.route}`);\n\t}\n\n\tif (context.error) {\n\t\tconst error = context.error.error;\n\t\tparts.push(\n\t\t\t`\\n! Last error: ${error.message}${context.error.meta?.markerId ? ` (related to marker: ${context.error.meta.markerId})` : \"\"}`,\n\t\t);\n\t\tparts.push(\n\t\t\t\"The user may want to retry the failed action. Look for markers related to the error.\",\n\t\t);\n\t}\n\n\tif (context.state && Object.keys(context.state).length > 0) {\n\t\tparts.push(`\\nApplication state: ${JSON.stringify(context.state)}`);\n\t}\n\n\tif (context.markers.length > 0) {\n\t\tparts.push(\"\\nAvailable UI elements (markers):\");\n\t\tfor (const marker of context.markers) {\n\t\t\tconst isInView = context.inViewMarkerIds?.includes(marker.id) ?? false;\n\t\t\tconst inViewStatus = isInView ? \" [IN VIEW]\" : \"\";\n\t\t\tlet markerLine = ` - ${marker.label} (ID: ${marker.id}, Type: ${marker.elementType})${inViewStatus}: ${marker.intent}`;\n\n\t\t\tif (marker.value !== undefined) {\n\t\t\t\tmarkerLine += `\\n Current value: \"${marker.value}\"`;\n\t\t\t}\n\n\t\t\tif (marker.options !== undefined) {\n\t\t\t\tconst optionsList = marker.options\n\t\t\t\t\t.map((opt) => `${opt.label} (${opt.value})`)\n\t\t\t\t\t.join(\", \");\n\t\t\t\tmarkerLine += `\\n Available options: ${optionsList}`;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tmarker.selectedOptions !== undefined &&\n\t\t\t\tmarker.selectedOptions.length > 0\n\t\t\t) {\n\t\t\t\tmarkerLine += `\\n Selected: ${marker.selectedOptions.join(\", \")}`;\n\t\t\t}\n\n\t\t\tparts.push(markerLine);\n\t\t}\n\t} else {\n\t\tparts.push(\"\\nNo UI elements are currently available.\");\n\t}\n\n\tif (context.inViewMarkerIds && context.inViewMarkerIds.length > 0) {\n\t\tparts.push(`\\nVisible markers: ${context.inViewMarkerIds.join(\", \")}`);\n\t}\n\n\treturn parts.join(\"\\n\");\n}\n\n/**\n * Convert tool registry to LangChain tools\n */\nfunction createLangChainTools(\n\ttoolRegistry: ToolRegistry,\n\tcontext: AgentRequest[\"context\"],\n): DynamicStructuredTool[] {\n\tconst tools: DynamicStructuredTool[] = [];\n\tconst toolDefinitions = toolRegistry.getToolDefinitions();\n\n\tfor (const toolDef of toolDefinitions) {\n\t\tconst def = toolDef.function;\n\n\t\tconst zodSchema: Record<string, z.ZodTypeAny> = {};\n\t\tfor (const [key, param] of Object.entries(\n\t\t\tdef.parameters.properties,\n\t\t) as Array<[string, { type: string; description: string }]>) {\n\t\t\tconst zodType =\n\t\t\t\tparam.type === \"string\"\n\t\t\t\t\t? z.string()\n\t\t\t\t\t: param.type === \"number\"\n\t\t\t\t\t\t? z.number()\n\t\t\t\t\t\t: param.type === \"boolean\"\n\t\t\t\t\t\t\t? z.boolean()\n\t\t\t\t\t\t\t: z.unknown();\n\t\t\tzodSchema[key] = zodType.describe(param.description);\n\t\t}\n\n\t\tconst schema = z.object(zodSchema);\n\n\t\tconst tool = new DynamicStructuredTool({\n\t\t\tname: def.name,\n\t\t\tdescription: def.description,\n\t\t\tschema,\n\t\t\tfunc: async (args: Record<string, unknown>) => {\n\t\t\t\tconst result = await toolRegistry.executeToolCall(\n\t\t\t\t\tdef.name,\n\t\t\t\t\targs,\n\t\t\t\t\tcontext,\n\t\t\t\t);\n\t\t\t\treturn JSON.stringify(result);\n\t\t\t},\n\t\t});\n\n\t\ttools.push(tool);\n\t}\n\n\treturn tools;\n}\n\n/**\n * Check if user input matches any marker and return matching marker info\n * Also checks if user is referring to a UI element (button, link, etc.) vs just a route\n */\nfunction findMatchingMarker(\n\tinput: string,\n\tmarkers: AgentRequest[\"context\"][\"markers\"],\n): {\n\tmarker: AgentRequest[\"context\"][\"markers\"][0];\n\tsearchText: string;\n\tisElementReference: boolean;\n} | null {\n\tconst lowerInput = input.toLowerCase().trim();\n\n\tconst hasNavigationLanguage =\n\t\tlowerInput.includes(\"go to\") ||\n\t\tlowerInput.includes(\"navigate to\") ||\n\t\tlowerInput.includes(\"open\") ||\n\t\tlowerInput.includes(\"take me to\");\n\n\tif (!hasNavigationLanguage) {\n\t\treturn null;\n\t}\n\n\tconst elementKeywords = [\n\t\t\"button\",\n\t\t\"link\",\n\t\t\"nav button\",\n\t\t\"navigation button\",\n\t\t\"nav link\",\n\t\t\"navigation link\",\n\t\t\"element\",\n\t\t\"item\",\n\t\t\"tab\",\n\t];\n\tconst isElementReference = elementKeywords.some((keyword) =>\n\t\tlowerInput.includes(keyword),\n\t);\n\n\tconst searchText = lowerInput\n\t\t.replace(/go to|navigate to|open|take me to/g, \"\")\n\t\t.trim();\n\n\tconst matchingMarker = markers.find((m) => {\n\t\tconst markerText = `${m.label} ${m.intent}`.toLowerCase();\n\t\treturn (\n\t\t\tmarkerText.includes(searchText) ||\n\t\t\tlowerInput.includes(m.label.toLowerCase()) ||\n\t\t\tlowerInput.includes(m.intent.toLowerCase())\n\t\t);\n\t});\n\n\treturn matchingMarker\n\t\t? { marker: matchingMarker, searchText, isElementReference }\n\t\t: null;\n}\n\n/**\n * LangChain response with tool calls\n */\ninterface LangChainResponseWithTools {\n\ttool_calls?: Array<{\n\t\tname?: string;\n\t\targs?: Record<string, unknown>;\n\t\tfunction?: {\n\t\t\tname?: string;\n\t\t\targuments?: string | Record<string, unknown>;\n\t\t};\n\t}>;\n\tadditional_kwargs?: {\n\t\ttool_calls?: Array<{\n\t\t\tname?: string;\n\t\t\targs?: Record<string, unknown>;\n\t\t\tfunction?: {\n\t\t\t\tname?: string;\n\t\t\t\targuments?: string | Record<string, unknown>;\n\t\t\t};\n\t\t}>;\n\t};\n}\n\n/**\n * Type guard to check if response has tool calls\n */\nfunction hasToolCalls(\n\tresponse: unknown,\n): response is LangChainResponseWithTools {\n\tconst kwargs = (response as LangChainResponseWithTools).additional_kwargs;\n\treturn (\n\t\ttypeof response === \"object\" &&\n\t\tresponse !== null &&\n\t\t(\"tool_calls\" in response ||\n\t\t\t(\"additional_kwargs\" in response &&\n\t\t\t\ttypeof kwargs === \"object\" &&\n\t\t\t\tkwargs !== null &&\n\t\t\t\t\"tool_calls\" in kwargs))\n\t);\n}\n\n/**\n * Run the LLM agent on the server\n */\nexport async function runAgent(\n\trequest: AgentRequest,\n\tconfig: ServerConfig,\n\ttoolRegistry: ToolRegistry = createDefaultToolRegistry(),\n): Promise<AgentResponse> {\n\tconst llm = await createLLM(config);\n\n\tconst markerMatch = findMatchingMarker(\n\t\trequest.input,\n\t\trequest.context.markers,\n\t);\n\n\tconst contextPrompt = formatContextForPrompt(request.context);\n\tconst langchainTools = createLangChainTools(toolRegistry, request.context);\n\n\tconst llmWithTools =\n\t\tlangchainTools.length > 0 && typeof llm.bindTools === \"function\"\n\t\t\t? llm.bindTools(langchainTools)\n\t\t\t: llm;\n\n\tlet recentMarkerContext = \"\";\n\tif (request.messages && request.messages.length > 0) {\n\t\tconst lastFewMessages = request.messages.slice(-4);\n\t\tfor (const msg of lastFewMessages) {\n\t\t\tfor (const marker of request.context.markers) {\n\t\t\t\tif (\n\t\t\t\t\tmsg.content.toLowerCase().includes(marker.label.toLowerCase()) ||\n\t\t\t\t\tmsg.content.toLowerCase().includes(marker.id.toLowerCase())\n\t\t\t\t) {\n\t\t\t\t\trecentMarkerContext += `\\nRecently discussed: ${marker.label} (${marker.id}) - ${marker.intent}`;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlet markerMatchGuidance = \"\";\n\tif (markerMatch) {\n\t\tconst marker = markerMatch.marker;\n\t\tconst isInView =\n\t\t\trequest.context.inViewMarkerIds?.includes(marker.id) ?? false;\n\t\tconst isLink = marker.elementType === \"a\";\n\n\t\tif (isLink && isInView) {\n\t\t\tmarkerMatchGuidance = `\\n\\n⚠️ Match found: \"${marker.label}\" (${marker.id}) is a visible link. Use 'click' tool.`;\n\t\t} else if (isLink && !isInView) {\n\t\t\tmarkerMatchGuidance = `\\n\\n🚨 Match found: \"${marker.label}\" (${marker.id}) is a link NOT in view. You MUST call BOTH 'scroll' and 'click' (in that order)—two tool calls. Do not call only scroll.`;\n\t\t} else if (!isInView) {\n\t\t\tmarkerMatchGuidance = `\\n\\n🚨 Match found: \"${marker.label}\" (${marker.id}) is NOT in view. You MUST call BOTH 'scroll' and 'click' (or the appropriate action) in that order—two tool calls. Do not call only scroll.`;\n\t\t} else {\n\t\t\tmarkerMatchGuidance = `\\n\\nMatch found: \"${marker.label}\" (${marker.id}) is an element. Use 'scroll' tool.`;\n\t\t}\n\t}\n\n\tconst systemPrompt = `You are a helpful AI agent embedded in a web application. Help users navigate, interact with UI elements, and resolve errors.\n\n${contextPrompt}${recentMarkerContext}${markerMatchGuidance}\n\nReading marker values:\n- You can read current values from markers in the context above\n- For input/textarea elements: The \"Current value\" field shows what's currently entered (password fields show \"[REDACTED]\" for privacy)\n- For select elements: The \"Available options\" shows all choices, and \"Selected\" shows what's currently selected\n- When users ask \"what's in [field]\" or \"what's the value of [marker]\", read the value from the context and respond with it\n- You don't need a tool to read values - they're already in the context provided above\n\nSecurity rules:\n- NEVER fill password fields - if a user asks to fill a password, politely explain that sending passwords is a security risk and they should enter it manually\n- Password field values are redacted as \"[REDACTED]\" in the context for privacy protection\n\nPronoun resolution:\n- When the user says \"it\", \"that\", \"this\", they're referring to the most recently discussed marker\n- Look at the recent conversation history (shown above as \"Recently discussed\") to identify which specific marker was discussed\n- Prefer specific interactive elements (buttons, inputs, links) over parent sections (those with \"section\" or \"slide\" in the label/id) when resolving pronouns\n- Example: If user asked about \"password input\" and then says \"highlight it\", use the password input marker (e.g. fill_demo_password), NOT the parent section marker (e.g. slide_fill_input)\n\nNavigation rules:\n- \"navigate to [element]\" = scroll to that element (use 'scroll' tool)\n- \"navigate to [route]\" = route navigation (use 'navigate' tool with route path)\n- If marker matches and is in inViewMarkerIds + elementType='a' → use 'click'\n- If no marker matches → use 'navigate' with route path\n- For affirmative responses after discussing a marker, interact with that marker using the appropriate tool.\n\nScroll-then-act rule (CRITICAL):\n- When the user wants to INTERACT with an element (click, press, increment, submit, fill, etc.) and the target marker is NOT in inViewMarkerIds: you MUST emit TWO tool calls in order—(1) 'scroll' to that marker, (2) the action ('click', 'fillInput', etc.). Emitting only 'scroll' is WRONG; the user asked for an action, so you must call both scroll and the action.\n- Prefer the specific interactive element for the action (e.g. the button click_demo_increment for \"increment counter\"), not a parent section (e.g. slide_click). Emit one scroll to the exact target element, then the action.\n- If the user only wants to see or navigate to an element (no click/fill), use a single 'scroll' to that element.\n\nRelative scrolling rules (for \"scroll to next\" or \"scroll to previous\"):\n- Markers are listed in document order (top to bottom) in the markers array\n- CRITICAL: Always skip markers that are in inViewMarkerIds - only scroll to markers NOT currently in view\n- For \"scroll to next\": Find the first marker in the markers array that comes after any currently visible markers and is NOT in inViewMarkerIds\n- For \"scroll to previous\": Find the first marker in the markers array that comes before any currently visible markers and is NOT in inViewMarkerIds\n- This prevents getting stuck on sections already in view\n\nMultiple actions rules:\n- When user says \"all\" (e.g., \"highlight all badges\", \"click all buttons\"), generate MULTIPLE tool calls - one for each matching marker\n- When user specifies a count (e.g., \"click 10 times\", \"increment counter 5 times\"), generate MULTIPLE tool calls - repeat the action the specified number of times\n- CRITICAL: You can and should call the same tool multiple times in a single response when the user requests multiple actions\n- For \"highlight all [type]\" or \"click all [type]\": Find all markers matching the type and generate one tool call per marker\n- For \"[action] [count] times\": Generate [count] identical tool calls`;\n\n\tconst conversationMessages: Array<{ role: string; content: string }> = [\n\t\t{ role: \"system\", content: systemPrompt },\n\t];\n\n\tif (request.messages && request.messages.length > 0) {\n\t\tconst recentMessages = request.messages.slice(-10);\n\t\tfor (const msg of recentMessages) {\n\t\t\tconversationMessages.push({\n\t\t\t\trole: msg.role,\n\t\t\t\tcontent: msg.content,\n\t\t\t});\n\t\t}\n\t}\n\n\tconversationMessages.push({ role: \"user\", content: request.input });\n\n\tconst response = await llmWithTools.invoke(conversationMessages);\n\n\tconst instructions: Instruction[] = [];\n\tlet reply = \"\";\n\n\tif (response.content) {\n\t\tif (typeof response.content === \"string\") {\n\t\t\treply = response.content;\n\t\t} else if (Array.isArray(response.content)) {\n\t\t\treply = response.content\n\t\t\t\t.map((c) => {\n\t\t\t\t\tif (typeof c === \"string\") return c;\n\t\t\t\t\tif (c && typeof c === \"object\" && \"text\" in c) return c.text;\n\t\t\t\t\treturn \"\";\n\t\t\t\t})\n\t\t\t\t.join(\"\");\n\t\t} else {\n\t\t\treply = String(response.content);\n\t\t}\n\t}\n\n\t// LangChain stores tool calls in response.tool_calls or response.additional_kwargs.tool_calls\n\tconst toolCallObjects: Array<{\n\t\tname?: string;\n\t\targs?: Record<string, unknown>;\n\t\tfunction?: {\n\t\t\tname?: string;\n\t\t\targuments?: string | Record<string, unknown>;\n\t\t};\n\t}> = hasToolCalls(response)\n\t\t? response.tool_calls || response.additional_kwargs?.tool_calls || []\n\t\t: [];\n\n\tfor (const toolCall of toolCallObjects) {\n\t\tconst toolName = toolCall.name || toolCall.function?.name;\n\t\tconst toolArgs =\n\t\t\ttoolCall.args ||\n\t\t\t(toolCall.function?.arguments\n\t\t\t\t? typeof toolCall.function.arguments === \"string\"\n\t\t\t\t\t? JSON.parse(toolCall.function.arguments)\n\t\t\t\t\t: toolCall.function.arguments\n\t\t\t\t: {});\n\n\t\tif (toolName) {\n\t\t\tconst converted = toolRegistry.convertToolCall({\n\t\t\t\ttype: \"function\",\n\t\t\t\tfunction: {\n\t\t\t\t\tname: toolName,\n\t\t\t\t\targuments: JSON.stringify(toolArgs),\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (converted) {\n\t\t\t\tinstructions.push(converted);\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await toolRegistry.executeToolCall(\n\t\t\t\t\t\ttoolName,\n\t\t\t\t\t\ttoolArgs,\n\t\t\t\t\t\trequest.context,\n\t\t\t\t\t);\n\t\t\t\t\tif (result && typeof result === \"object\" && \"action\" in result) {\n\t\t\t\t\t\tinstructions.push(result as Instruction);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Error executing tool ${toolName}:`, error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\treply: reply || \"I'm here to help!\",\n\t\tinstructions: instructions.length > 0 ? instructions : undefined,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ToolRegistry } from "./tool-registry.mjs";
|
|
2
|
+
import { ServerConfig } from "./types.mjs";
|
|
3
|
+
import { FastifyInstance, FastifyPluginOptions } from "fastify";
|
|
4
|
+
|
|
5
|
+
//#region src/fastify.d.ts
|
|
6
|
+
interface FastifyAi11yOptions extends FastifyPluginOptions {
|
|
7
|
+
config: ServerConfig;
|
|
8
|
+
toolRegistry?: ToolRegistry;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Fastify plugin for ai11y server
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import Fastify from 'fastify';
|
|
16
|
+
* import { ai11yPlugin } from '@ai11y/agent/fastify';
|
|
17
|
+
*
|
|
18
|
+
* const fastify = Fastify();
|
|
19
|
+
*
|
|
20
|
+
* await fastify.register(ai11yPlugin, {
|
|
21
|
+
* config: {
|
|
22
|
+
* provider: 'openai',
|
|
23
|
+
* apiKey: process.env.OPENAI_API_KEY!,
|
|
24
|
+
* model: 'gpt-4o-mini'
|
|
25
|
+
* }
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* await fastify.listen({ port: 3000 });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function ai11yPlugin(fastify: FastifyInstance, options: FastifyAi11yOptions): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Helper function to create a tool registry with custom tools.
|
|
34
|
+
* Returns a ToolRegistry instance that supports method chaining.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const registry = createToolRegistry()
|
|
39
|
+
* .register({
|
|
40
|
+
* name: "custom_action",
|
|
41
|
+
* description: "Perform a custom action",
|
|
42
|
+
* parameters: {
|
|
43
|
+
* type: "object",
|
|
44
|
+
* properties: {
|
|
45
|
+
* param: { type: "string", description: "A parameter" }
|
|
46
|
+
* },
|
|
47
|
+
* required: ["param"]
|
|
48
|
+
* }
|
|
49
|
+
* }, async (args) => {
|
|
50
|
+
* // Execute custom logic
|
|
51
|
+
* return { success: true };
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function createToolRegistry(): ToolRegistry;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { type ServerConfig, ai11yPlugin, createToolRegistry };
|
|
58
|
+
//# sourceMappingURL=fastify.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.d.mts","names":[],"sources":["../src/fastify.ts"],"sourcesContent":[],"mappings":";;;;;UAcU,mBAAA,SAA4B;UAC7B;EADC,YAAA,CAAA,EAEM,YAFc;;;;;AA0B9B;;;;;AAkEA;;;;;;;;;;;;;iBAlEsB,WAAA,UACZ,0BACA,sBAAmB;;;;;;;;;;;;;;;;;;;;;;;;iBAgEb,kBAAA,CAAA,GAAsB"}
|
package/dist/fastify.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createDefaultToolRegistry } from "./tool-registry.mjs";
|
|
2
|
+
import { runAgent } from "./agent.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/fastify.ts
|
|
5
|
+
/**
|
|
6
|
+
* Fastify plugin for ai11y server
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import Fastify from 'fastify';
|
|
11
|
+
* import { ai11yPlugin } from '@ai11y/agent/fastify';
|
|
12
|
+
*
|
|
13
|
+
* const fastify = Fastify();
|
|
14
|
+
*
|
|
15
|
+
* await fastify.register(ai11yPlugin, {
|
|
16
|
+
* config: {
|
|
17
|
+
* provider: 'openai',
|
|
18
|
+
* apiKey: process.env.OPENAI_API_KEY!,
|
|
19
|
+
* model: 'gpt-4o-mini'
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* await fastify.listen({ port: 3000 });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
async function ai11yPlugin(fastify, options) {
|
|
27
|
+
const { config, toolRegistry = createDefaultToolRegistry() } = options;
|
|
28
|
+
if (!config.apiKey) throw new Error("API key is required");
|
|
29
|
+
/**
|
|
30
|
+
* POST /ai11y/agent
|
|
31
|
+
* Main endpoint for agent requests
|
|
32
|
+
*/
|
|
33
|
+
fastify.post("/ai11y/agent", async (request, reply) => {
|
|
34
|
+
try {
|
|
35
|
+
const response = await runAgent(request.body, config, toolRegistry);
|
|
36
|
+
return reply.send(response);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
fastify.log.error(error, "Error processing agent request");
|
|
39
|
+
return reply.status(500).send({
|
|
40
|
+
error: "Failed to process agent request",
|
|
41
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* GET /ai11y/health
|
|
47
|
+
* Health check endpoint
|
|
48
|
+
*/
|
|
49
|
+
fastify.get("/ai11y/health", async (_request, reply) => {
|
|
50
|
+
return reply.send({ status: "ok" });
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Helper function to create a tool registry with custom tools.
|
|
55
|
+
* Returns a ToolRegistry instance that supports method chaining.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* const registry = createToolRegistry()
|
|
60
|
+
* .register({
|
|
61
|
+
* name: "custom_action",
|
|
62
|
+
* description: "Perform a custom action",
|
|
63
|
+
* parameters: {
|
|
64
|
+
* type: "object",
|
|
65
|
+
* properties: {
|
|
66
|
+
* param: { type: "string", description: "A parameter" }
|
|
67
|
+
* },
|
|
68
|
+
* required: ["param"]
|
|
69
|
+
* }
|
|
70
|
+
* }, async (args) => {
|
|
71
|
+
* // Execute custom logic
|
|
72
|
+
* return { success: true };
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function createToolRegistry() {
|
|
77
|
+
return createDefaultToolRegistry();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { ai11yPlugin, createToolRegistry };
|
|
82
|
+
//# sourceMappingURL=fastify.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.mjs","names":[],"sources":["../src/fastify.ts"],"sourcesContent":["import type { AgentRequest } from \"@ai11y/core\";\nimport type {\n\tFastifyInstance,\n\tFastifyPluginOptions,\n\tFastifyReply,\n\tFastifyRequest,\n} from \"fastify\";\nimport { runAgent } from \"./agent.js\";\nimport {\n\tcreateDefaultToolRegistry,\n\ttype ToolRegistry,\n} from \"./tool-registry.js\";\nimport type { ServerConfig } from \"./types.js\";\n\ninterface FastifyAi11yOptions extends FastifyPluginOptions {\n\tconfig: ServerConfig;\n\ttoolRegistry?: ToolRegistry;\n}\n\n/**\n * Fastify plugin for ai11y server\n *\n * @example\n * ```ts\n * import Fastify from 'fastify';\n * import { ai11yPlugin } from '@ai11y/agent/fastify';\n *\n * const fastify = Fastify();\n *\n * await fastify.register(ai11yPlugin, {\n * config: {\n * provider: 'openai',\n * apiKey: process.env.OPENAI_API_KEY!,\n * model: 'gpt-4o-mini'\n * }\n * });\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\nexport async function ai11yPlugin(\n\tfastify: FastifyInstance,\n\toptions: FastifyAi11yOptions,\n) {\n\tconst { config, toolRegistry = createDefaultToolRegistry() } = options;\n\n\t// Validate config\n\tif (!config.apiKey) {\n\t\tthrow new Error(\"API key is required\");\n\t}\n\n\t/**\n\t * POST /ai11y/agent\n\t * Main endpoint for agent requests\n\t */\n\tfastify.post<{ Body: AgentRequest }>(\n\t\t\"/ai11y/agent\",\n\t\tasync (\n\t\t\trequest: FastifyRequest<{ Body: AgentRequest }>,\n\t\t\treply: FastifyReply,\n\t\t) => {\n\t\t\ttry {\n\t\t\t\tconst response = await runAgent(request.body, config, toolRegistry);\n\t\t\t\treturn reply.send(response);\n\t\t\t} catch (error) {\n\t\t\t\tfastify.log.error(error, \"Error processing agent request\");\n\t\t\t\treturn reply.status(500).send({\n\t\t\t\t\terror: \"Failed to process agent request\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : \"Unknown error\",\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t);\n\n\t/**\n\t * GET /ai11y/health\n\t * Health check endpoint\n\t */\n\tfastify.get(\"/ai11y/health\", async (_request, reply) => {\n\t\treturn reply.send({ status: \"ok\" });\n\t});\n}\n\n/**\n * Helper function to create a tool registry with custom tools.\n * Returns a ToolRegistry instance that supports method chaining.\n *\n * @example\n * ```ts\n * const registry = createToolRegistry()\n * .register({\n * name: \"custom_action\",\n * description: \"Perform a custom action\",\n * parameters: {\n * type: \"object\",\n * properties: {\n * param: { type: \"string\", description: \"A parameter\" }\n * },\n * required: [\"param\"]\n * }\n * }, async (args) => {\n * // Execute custom logic\n * return { success: true };\n * });\n * ```\n */\nexport function createToolRegistry(): ToolRegistry {\n\treturn createDefaultToolRegistry();\n}\n\nexport type { ServerConfig } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,eAAsB,YACrB,SACA,SACC;CACD,MAAM,EAAE,QAAQ,eAAe,2BAA2B,KAAK;AAG/D,KAAI,CAAC,OAAO,OACX,OAAM,IAAI,MAAM,sBAAsB;;;;;AAOvC,SAAQ,KACP,gBACA,OACC,SACA,UACI;AACJ,MAAI;GACH,MAAM,WAAW,MAAM,SAAS,QAAQ,MAAM,QAAQ,aAAa;AACnE,UAAO,MAAM,KAAK,SAAS;WACnB,OAAO;AACf,WAAQ,IAAI,MAAM,OAAO,iCAAiC;AAC1D,UAAO,MAAM,OAAO,IAAI,CAAC,KAAK;IAC7B,OAAO;IACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;IAClD,CAAC;;GAGJ;;;;;AAMD,SAAQ,IAAI,iBAAiB,OAAO,UAAU,UAAU;AACvD,SAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,CAAC;GAClC;;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,SAAgB,qBAAmC;AAClD,QAAO,2BAA2B"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ToolRegistry, createDefaultToolRegistry } from "./tool-registry.mjs";
|
|
2
|
+
import { ServerConfig } from "./types.mjs";
|
|
3
|
+
import { runAgent } from "./agent.mjs";
|
|
4
|
+
import { createLLM } from "./llm-provider.mjs";
|
|
5
|
+
export { type ServerConfig, ToolRegistry, createDefaultToolRegistry, createLLM, runAgent };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ServerConfig } from "./types.mjs";
|
|
2
|
+
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
3
|
+
|
|
4
|
+
//#region src/llm-provider.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a LangChain LLM instance for OpenAI
|
|
8
|
+
*/
|
|
9
|
+
declare function createLLM(config: ServerConfig): Promise<BaseChatModel>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { createLLM };
|
|
12
|
+
//# sourceMappingURL=llm-provider.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-provider.d.mts","names":[],"sources":["../src/llm-provider.ts"],"sourcesContent":[],"mappings":";;;;;;;;iBAWsB,SAAA,SAAkB,eAAe,QAAQ"}
|