@alexkroman1/aai 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cli.js +3436 -0
- package/package.json +78 -0
- package/sdk/_internal_types.ts +89 -0
- package/sdk/_mock_ws.ts +172 -0
- package/sdk/_timeout.ts +24 -0
- package/sdk/builtin_tools.ts +309 -0
- package/sdk/capnweb.ts +341 -0
- package/sdk/define_agent.ts +70 -0
- package/sdk/direct_executor.ts +195 -0
- package/sdk/kv.ts +183 -0
- package/sdk/mod.ts +35 -0
- package/sdk/protocol.ts +313 -0
- package/sdk/runtime.ts +65 -0
- package/sdk/s2s.ts +271 -0
- package/sdk/server.ts +198 -0
- package/sdk/session.ts +438 -0
- package/sdk/system_prompt.ts +47 -0
- package/sdk/types.ts +406 -0
- package/sdk/vector.ts +133 -0
- package/sdk/winterc_server.ts +141 -0
- package/sdk/worker_entry.ts +99 -0
- package/sdk/worker_shim.ts +170 -0
- package/sdk/ws_handler.ts +190 -0
- package/templates/_shared/.env.example +5 -0
- package/templates/_shared/package.json +17 -0
- package/templates/code-interpreter/agent.ts +27 -0
- package/templates/code-interpreter/client.tsx +2 -0
- package/templates/dispatch-center/agent.ts +1536 -0
- package/templates/dispatch-center/client.tsx +504 -0
- package/templates/embedded-assets/agent.ts +49 -0
- package/templates/embedded-assets/client.tsx +2 -0
- package/templates/embedded-assets/knowledge.json +20 -0
- package/templates/health-assistant/agent.ts +160 -0
- package/templates/health-assistant/client.tsx +2 -0
- package/templates/infocom-adventure/agent.ts +164 -0
- package/templates/infocom-adventure/client.tsx +299 -0
- package/templates/math-buddy/agent.ts +21 -0
- package/templates/math-buddy/client.tsx +2 -0
- package/templates/memory-agent/agent.ts +74 -0
- package/templates/memory-agent/client.tsx +2 -0
- package/templates/night-owl/agent.ts +98 -0
- package/templates/night-owl/client.tsx +28 -0
- package/templates/personal-finance/agent.ts +26 -0
- package/templates/personal-finance/client.tsx +2 -0
- package/templates/simple/agent.ts +6 -0
- package/templates/simple/client.tsx +2 -0
- package/templates/smart-research/agent.ts +164 -0
- package/templates/smart-research/client.tsx +2 -0
- package/templates/support/README.md +62 -0
- package/templates/support/agent.ts +19 -0
- package/templates/support/client.tsx +2 -0
- package/templates/travel-concierge/agent.ts +29 -0
- package/templates/travel-concierge/client.tsx +2 -0
- package/templates/web-researcher/agent.ts +17 -0
- package/templates/web-researcher/client.tsx +2 -0
- package/ui/_components/app.tsx +37 -0
- package/ui/_components/chat_view.tsx +36 -0
- package/ui/_components/controls.tsx +32 -0
- package/ui/_components/error_banner.tsx +18 -0
- package/ui/_components/message_bubble.tsx +21 -0
- package/ui/_components/message_list.tsx +61 -0
- package/ui/_components/state_indicator.tsx +17 -0
- package/ui/_components/thinking_indicator.tsx +19 -0
- package/ui/_components/tool_call_block.tsx +110 -0
- package/ui/_components/tool_icons.tsx +101 -0
- package/ui/_components/transcript.tsx +20 -0
- package/ui/audio.ts +170 -0
- package/ui/components.ts +49 -0
- package/ui/components_mod.ts +37 -0
- package/ui/mod.ts +48 -0
- package/ui/mount.tsx +112 -0
- package/ui/mount_context.ts +19 -0
- package/ui/session.ts +456 -0
- package/ui/session_mod.ts +27 -0
- package/ui/signals.ts +111 -0
- package/ui/types.ts +50 -0
- package/ui/worklets/capture-processor.js +62 -0
- package/ui/worklets/playback-processor.js +110 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export default defineAgent({
|
|
5
|
+
name: "Memory Agent",
|
|
6
|
+
instructions: `You are a helpful assistant with persistent memory. You can \
|
|
7
|
+
remember facts, preferences, and notes across conversations.
|
|
8
|
+
|
|
9
|
+
When the user tells you something worth remembering, save it with a descriptive \
|
|
10
|
+
key. When they ask about something you might have saved, look it up.
|
|
11
|
+
|
|
12
|
+
Use save_memory for storing information and recall_memory for retrieving it. \
|
|
13
|
+
Use list_memories to see what you have stored. Be proactive about saving \
|
|
14
|
+
important details the user shares — names, preferences, ongoing projects, etc.
|
|
15
|
+
|
|
16
|
+
Keep responses concise and conversational. Never say "I saved that to my \
|
|
17
|
+
database" — just confirm naturally, like "Got it, I'll remember that."`,
|
|
18
|
+
greeting:
|
|
19
|
+
"Hey there. I'm an assistant with a long-term memory. Tell me things you want me to remember, and I'll recall them in future conversations.",
|
|
20
|
+
builtinTools: ["web_search"],
|
|
21
|
+
tools: {
|
|
22
|
+
save_memory: {
|
|
23
|
+
description:
|
|
24
|
+
"Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
|
|
25
|
+
parameters: z.object({
|
|
26
|
+
key: z.string().describe(
|
|
27
|
+
"A descriptive key for this memory (e.g. 'user:name', 'preference:color')",
|
|
28
|
+
),
|
|
29
|
+
value: z.string().describe("The information to remember"),
|
|
30
|
+
}),
|
|
31
|
+
execute: async (
|
|
32
|
+
{ key, value }: { key: string; value: string },
|
|
33
|
+
ctx,
|
|
34
|
+
) => {
|
|
35
|
+
await ctx.kv.set(key, value);
|
|
36
|
+
return { saved: key };
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
recall_memory: {
|
|
40
|
+
description: "Retrieve a previously saved memory by its key.",
|
|
41
|
+
parameters: z.object({
|
|
42
|
+
key: z.string().describe("The key to look up"),
|
|
43
|
+
}),
|
|
44
|
+
execute: async ({ key }: { key: string }, ctx) => {
|
|
45
|
+
const value = await ctx.kv.get(key);
|
|
46
|
+
if (value === null) return { found: false, key };
|
|
47
|
+
return { found: true, key, value };
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
list_memories: {
|
|
51
|
+
description:
|
|
52
|
+
"List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
|
|
53
|
+
parameters: z.object({
|
|
54
|
+
prefix: z.string().describe(
|
|
55
|
+
"Prefix to filter keys (e.g. 'user:'). Use empty string for all.",
|
|
56
|
+
).optional(),
|
|
57
|
+
}),
|
|
58
|
+
execute: async ({ prefix }: { prefix?: string }, ctx) => {
|
|
59
|
+
const entries = await ctx.kv.list(prefix ?? "");
|
|
60
|
+
return { count: entries.length, keys: entries.map((e) => e.key) };
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
forget_memory: {
|
|
64
|
+
description: "Delete a previously saved memory by its key.",
|
|
65
|
+
parameters: z.object({
|
|
66
|
+
key: z.string().describe("The key to delete"),
|
|
67
|
+
}),
|
|
68
|
+
execute: async ({ key }: { key: string }, ctx) => {
|
|
69
|
+
await ctx.kv.delete(key);
|
|
70
|
+
return { deleted: key };
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
const PICKS: Record<string, Record<string, string[]>> = {
|
|
5
|
+
movie: {
|
|
6
|
+
chill: ["Lost in Translation", "The Grand Budapest Hotel", "Amelie"],
|
|
7
|
+
intense: ["Inception", "Interstellar", "The Dark Knight"],
|
|
8
|
+
cozy: ["When Harry Met Sally", "The Holiday", "Paddington 2"],
|
|
9
|
+
spooky: ["The Shining", "Get Out", "Hereditary"],
|
|
10
|
+
funny: ["The Big Lebowski", "Airplane!", "Superbad"],
|
|
11
|
+
},
|
|
12
|
+
music: {
|
|
13
|
+
chill: [
|
|
14
|
+
"Khruangbin — Con Todo El Mundo",
|
|
15
|
+
"Tycho — Dive",
|
|
16
|
+
"Bonobo — Migration",
|
|
17
|
+
],
|
|
18
|
+
intense: [
|
|
19
|
+
"Radiohead — OK Computer",
|
|
20
|
+
"Tool — Lateralus",
|
|
21
|
+
"Deftones — White Pony",
|
|
22
|
+
],
|
|
23
|
+
cozy: [
|
|
24
|
+
"Norah Jones — Come Away with Me",
|
|
25
|
+
"Iron & Wine — Our Endless Numbered Days",
|
|
26
|
+
"Bon Iver — For Emma, Forever Ago",
|
|
27
|
+
],
|
|
28
|
+
spooky: [
|
|
29
|
+
"Portishead — Dummy",
|
|
30
|
+
"Massive Attack — Mezzanine",
|
|
31
|
+
"Boards of Canada — Music Has the Right to Children",
|
|
32
|
+
],
|
|
33
|
+
funny: [
|
|
34
|
+
"Weird Al — Running with Scissors",
|
|
35
|
+
"Flight of the Conchords — S/T",
|
|
36
|
+
"Tenacious D — S/T",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
book: {
|
|
40
|
+
chill: [
|
|
41
|
+
"Norwegian Wood — Murakami",
|
|
42
|
+
"The Alchemist — Coelho",
|
|
43
|
+
"Siddhartha — Hesse",
|
|
44
|
+
],
|
|
45
|
+
intense: [
|
|
46
|
+
"Blood Meridian — McCarthy",
|
|
47
|
+
"House of Leaves — Danielewski",
|
|
48
|
+
"Neuromancer — Gibson",
|
|
49
|
+
],
|
|
50
|
+
cozy: [
|
|
51
|
+
"The House in the Cerulean Sea — Klune",
|
|
52
|
+
"A Man Called Ove — Backman",
|
|
53
|
+
"Anxious People — Backman",
|
|
54
|
+
],
|
|
55
|
+
spooky: [
|
|
56
|
+
"The Haunting of Hill House — Jackson",
|
|
57
|
+
"Mexican Gothic — Moreno-Garcia",
|
|
58
|
+
"The Turn of the Screw — James",
|
|
59
|
+
],
|
|
60
|
+
funny: [
|
|
61
|
+
"Good Omens — Pratchett & Gaiman",
|
|
62
|
+
"Hitchhiker's Guide — Adams",
|
|
63
|
+
"Catch-22 — Heller",
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default defineAgent({
|
|
69
|
+
name: "Night Owl",
|
|
70
|
+
instructions:
|
|
71
|
+
`You are Night Owl, a cozy evening companion. You help people wind down, recommend entertainment, and share interesting facts about the night sky. Keep your tone warm and relaxed. Use short, conversational responses.
|
|
72
|
+
|
|
73
|
+
Use run_code for sleep calculations:
|
|
74
|
+
- Each sleep cycle is 90 minutes, plus 15 minutes to fall asleep
|
|
75
|
+
- Bedtime = wake_time - (cycles * 90 + 15) minutes
|
|
76
|
+
- If result is negative, add 1440 (24 hours in minutes)
|
|
77
|
+
- Format as HH:MM`,
|
|
78
|
+
greeting:
|
|
79
|
+
"Hey there, night owl. Try asking me for a cozy movie recommendation, or tell me what time you need to wake up and I'll calculate the best time to fall asleep.",
|
|
80
|
+
builtinTools: ["run_code"],
|
|
81
|
+
tools: {
|
|
82
|
+
recommend: {
|
|
83
|
+
description:
|
|
84
|
+
"Get recommendations for movies, music, or books based on mood.",
|
|
85
|
+
parameters: z.object({
|
|
86
|
+
category: z.enum(["movie", "music", "book"]),
|
|
87
|
+
mood: z.enum(["chill", "intense", "cozy", "spooky", "funny"]),
|
|
88
|
+
}),
|
|
89
|
+
execute: ({ category, mood }) => {
|
|
90
|
+
return {
|
|
91
|
+
category,
|
|
92
|
+
mood,
|
|
93
|
+
picks: PICKS[category]?.[mood] ?? [],
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ChatView, mount, useSession } from "@alexkroman1/aai/ui";
|
|
2
|
+
|
|
3
|
+
function NightOwl() {
|
|
4
|
+
const { started, start } = useSession();
|
|
5
|
+
|
|
6
|
+
if (!started.value) {
|
|
7
|
+
return (
|
|
8
|
+
<div class="flex items-center justify-center h-screen bg-aai-bg font-aai">
|
|
9
|
+
<div class="flex flex-col items-center gap-6 bg-aai-surface border border-aai-border rounded-lg px-12 py-10">
|
|
10
|
+
<div class="text-5xl">🦉</div>
|
|
11
|
+
<h1 class="text-xl font-semibold text-aai-text m-0">Night Owl</h1>
|
|
12
|
+
<p class="text-sm text-aai-text-muted m-0">your evening companion</p>
|
|
13
|
+
<button
|
|
14
|
+
type="button"
|
|
15
|
+
class="mt-2 px-8 py-3 rounded-aai text-sm font-medium cursor-pointer bg-aai-primary text-white border-none"
|
|
16
|
+
onClick={start}
|
|
17
|
+
>
|
|
18
|
+
Start Conversation
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return <ChatView />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mount(NightOwl);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
|
|
3
|
+
export default defineAgent({
|
|
4
|
+
name: "Penny",
|
|
5
|
+
greeting:
|
|
6
|
+
"Hey, I'm Penny, your personal finance helper. Try asking me something like, what's 100 dollars in euros, what's the price of bitcoin, or help me split a 120 dollar bill four ways with 20 percent tip.",
|
|
7
|
+
instructions:
|
|
8
|
+
`You are Penny, a friendly personal finance assistant. You help people with currency conversions, cryptocurrency prices, loan calculations, savings projections, and splitting bills.
|
|
9
|
+
|
|
10
|
+
Rules:
|
|
11
|
+
- Always show your math clearly when explaining calculations
|
|
12
|
+
- When discussing investments or crypto, remind users that prices fluctuate and this is not financial advice
|
|
13
|
+
- Be encouraging about savings goals
|
|
14
|
+
- Keep responses concise — this is a voice conversation
|
|
15
|
+
- Round dollar amounts to two decimal places for clarity
|
|
16
|
+
|
|
17
|
+
API endpoints (use fetch_json):
|
|
18
|
+
- Currency rates: https://open.er-api.com/v6/latest/{CODE} — returns { rates: { USD: 1.0, EUR: 0.85, ... } }
|
|
19
|
+
- Crypto prices: https://api.coingecko.com/api/v3/simple/price?ids={coin}&vs_currencies={cur}&include_24hr_change=true&include_market_cap=true
|
|
20
|
+
|
|
21
|
+
Math calculations (use run_code):
|
|
22
|
+
- Compound interest: FV = principal * (1 + rate/n)^(n*years) + monthly * ((1 + rate/n)^(n*years) - 1) / (rate/n)
|
|
23
|
+
- Loan payment: M = P * (r(1+r)^n) / ((1+r)^n - 1) where r = annual_rate/12, n = years*12
|
|
24
|
+
- Tip calculator: tip = bill * percent/100, per_person = (bill + tip) / people`,
|
|
25
|
+
builtinTools: ["run_code", "fetch_json"],
|
|
26
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { HookContext, StepInfo } from "@alexkroman1/aai/types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Smart Research Agent — demonstrates all 5 advanced features:
|
|
7
|
+
* 1. toolChoice: "required" — forces the LLM to use tools every step
|
|
8
|
+
* 2. ctx.messages — tools can read conversation history
|
|
9
|
+
* 3. onStep — logs each step's tool calls
|
|
10
|
+
* 4. onBeforeStep — restricts available tools based on research phase
|
|
11
|
+
* 5. maxSteps as function — adapts max steps based on session complexity
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type ResearchState = {
|
|
15
|
+
phase: "gather" | "analyze" | "respond";
|
|
16
|
+
sources: string[];
|
|
17
|
+
stepCount: number;
|
|
18
|
+
complexity: "simple" | "deep";
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default defineAgent({
|
|
22
|
+
name: "Smart Research Agent",
|
|
23
|
+
instructions: `You are a research assistant that gathers information, \
|
|
24
|
+
analyzes it, then responds. You work in three phases:
|
|
25
|
+
1. Gather: Use search and fetch tools to collect information.
|
|
26
|
+
2. Analyze: Use the analyze tool to synthesize your findings.
|
|
27
|
+
3. Respond: Deliver your final answer.
|
|
28
|
+
|
|
29
|
+
Always search first, then analyze, then answer. Be thorough but concise.`,
|
|
30
|
+
greeting:
|
|
31
|
+
"I'm your research assistant. Ask me anything and I'll dig into it.",
|
|
32
|
+
builtinTools: ["web_search"],
|
|
33
|
+
|
|
34
|
+
// Feature 1: toolChoice — force the LLM to always use a tool
|
|
35
|
+
toolChoice: "required",
|
|
36
|
+
|
|
37
|
+
// Feature 5: maxSteps as function — more steps for complex research
|
|
38
|
+
maxSteps: (ctx: HookContext<ResearchState>) => {
|
|
39
|
+
const state = ctx.state;
|
|
40
|
+
return state.complexity === "deep" ? 10 : 5;
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
state: (): ResearchState => ({
|
|
44
|
+
phase: "gather",
|
|
45
|
+
sources: [],
|
|
46
|
+
stepCount: 0,
|
|
47
|
+
complexity: "simple",
|
|
48
|
+
}),
|
|
49
|
+
|
|
50
|
+
// Feature 3: onStep — track what tools were called each step
|
|
51
|
+
onStep: (step: StepInfo, ctx: HookContext<ResearchState>) => {
|
|
52
|
+
const state = ctx.state;
|
|
53
|
+
state.stepCount++;
|
|
54
|
+
for (const tc of step.toolCalls) {
|
|
55
|
+
console.log(
|
|
56
|
+
`[step ${step.stepNumber}] ${tc.toolName} (phase: ${state.phase})`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Feature 4: onBeforeStep — restrict tools per research phase
|
|
62
|
+
onBeforeStep: (
|
|
63
|
+
_stepNumber: number,
|
|
64
|
+
ctx: HookContext<ResearchState>,
|
|
65
|
+
) => {
|
|
66
|
+
const state = ctx.state;
|
|
67
|
+
if (state.phase === "gather") {
|
|
68
|
+
return {
|
|
69
|
+
activeTools: [
|
|
70
|
+
"web_search",
|
|
71
|
+
"save_source",
|
|
72
|
+
"mark_complex",
|
|
73
|
+
"advance_phase",
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (state.phase === "analyze") {
|
|
78
|
+
return {
|
|
79
|
+
activeTools: [
|
|
80
|
+
"analyze",
|
|
81
|
+
"conversation_summary",
|
|
82
|
+
"advance_phase",
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// respond phase: no tools needed, LLM responds with text directly
|
|
87
|
+
return { activeTools: [] };
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
tools: {
|
|
91
|
+
save_source: {
|
|
92
|
+
description: "Save a source URL found during research for later analysis",
|
|
93
|
+
parameters: z.object({
|
|
94
|
+
url: z.string().describe("The source URL"),
|
|
95
|
+
title: z.string().describe("Brief title or description"),
|
|
96
|
+
}),
|
|
97
|
+
execute: (args, ctx) => {
|
|
98
|
+
const state = ctx.state;
|
|
99
|
+
state.sources.push(`${args.title}: ${args.url}`);
|
|
100
|
+
return { saved: true, totalSources: state.sources.length };
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
mark_complex: {
|
|
105
|
+
description:
|
|
106
|
+
"Mark this research query as complex, allowing more search steps",
|
|
107
|
+
execute: (_args, ctx) => {
|
|
108
|
+
const state = ctx.state;
|
|
109
|
+
state.complexity = "deep";
|
|
110
|
+
return { complexity: "deep", maxSteps: 10 };
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
advance_phase: {
|
|
115
|
+
description:
|
|
116
|
+
"Move to the next research phase (gather -> analyze -> respond)",
|
|
117
|
+
execute: (_args, ctx) => {
|
|
118
|
+
const state = ctx.state;
|
|
119
|
+
if (state.phase === "gather") {
|
|
120
|
+
state.phase = "analyze";
|
|
121
|
+
} else if (state.phase === "analyze") {
|
|
122
|
+
state.phase = "respond";
|
|
123
|
+
}
|
|
124
|
+
return { phase: state.phase };
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// Feature 2: ctx.messages — access conversation history in tools
|
|
129
|
+
analyze: {
|
|
130
|
+
description:
|
|
131
|
+
"Analyze all gathered sources and conversation context to form a conclusion",
|
|
132
|
+
parameters: z.object({
|
|
133
|
+
focus: z.string().describe("What aspect to focus the analysis on"),
|
|
134
|
+
}),
|
|
135
|
+
execute: (args, ctx) => {
|
|
136
|
+
const state = ctx.state;
|
|
137
|
+
// Use ctx.messages to see what's been discussed
|
|
138
|
+
const userMessages = ctx.messages.filter((m) => m.role === "user");
|
|
139
|
+
return {
|
|
140
|
+
focus: args.focus,
|
|
141
|
+
sources: state.sources,
|
|
142
|
+
conversationTurns: userMessages.length,
|
|
143
|
+
totalMessages: ctx.messages.length,
|
|
144
|
+
phase: state.phase,
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
conversation_summary: {
|
|
150
|
+
description: "Get a summary of the conversation so far",
|
|
151
|
+
execute: (_args, ctx) => {
|
|
152
|
+
const msgs = ctx.messages;
|
|
153
|
+
return {
|
|
154
|
+
totalMessages: msgs.length,
|
|
155
|
+
byRole: {
|
|
156
|
+
user: msgs.filter((m) => m.role === "user").length,
|
|
157
|
+
assistant: msgs.filter((m) => m.role === "assistant").length,
|
|
158
|
+
tool: msgs.filter((m) => m.role === "tool").length,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# AssemblyAI Support Agent
|
|
2
|
+
|
|
3
|
+
A voice-powered support agent for [AssemblyAI](https://assemblyai.com), built
|
|
4
|
+
with [aai](https://github.com/anthropics/aai).
|
|
5
|
+
|
|
6
|
+
## Getting started
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
aai deploy # Bundle and deploy
|
|
10
|
+
aai deploy -y # Deploy without prompts
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Ingesting documentation into the vector store
|
|
14
|
+
|
|
15
|
+
This agent uses `vector_search` to answer questions from AssemblyAI's docs.
|
|
16
|
+
Before it can answer anything, you need to ingest the documentation:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
aai rag https://assemblyai.com/docs/llms-full.txt
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This fetches AssemblyAI's `llms-full.txt` (a single file containing all their
|
|
23
|
+
documentation), chunks it, and upserts the chunks into the vector store.
|
|
24
|
+
|
|
25
|
+
### Options
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
# Custom chunk size (default: 512 tokens)
|
|
29
|
+
aai rag https://assemblyai.com/docs/llms-full.txt --chunk-size 256
|
|
30
|
+
|
|
31
|
+
# Target a specific server
|
|
32
|
+
aai rag https://assemblyai.com/docs/llms-full.txt --server http://localhost:3100
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### How it works
|
|
36
|
+
|
|
37
|
+
1. `aai rag` fetches the `llms-full.txt` file from the URL
|
|
38
|
+
2. It splits the content into chunks (~512 tokens each by default)
|
|
39
|
+
3. Each chunk is upserted into the vector store, scoped to this agent
|
|
40
|
+
4. At runtime, the `vector_search` builtin tool queries the vector store to find
|
|
41
|
+
relevant documentation chunks for each user question
|
|
42
|
+
|
|
43
|
+
### Re-ingesting
|
|
44
|
+
|
|
45
|
+
Run the same `aai rag` command again to update the vector store with fresh
|
|
46
|
+
documentation. Chunks are keyed by content, so unchanged pages won't be
|
|
47
|
+
duplicated.
|
|
48
|
+
|
|
49
|
+
## Environment variables
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
aai env add MY_KEY # Set a secret (prompts for value)
|
|
53
|
+
aai env ls # List secret names
|
|
54
|
+
aai env pull # Pull names into .env for reference
|
|
55
|
+
aai env rm MY_KEY # Remove a secret
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Access secrets in your agent via `ctx.env.MY_KEY`.
|
|
59
|
+
|
|
60
|
+
## Learn more
|
|
61
|
+
|
|
62
|
+
See `CLAUDE.md` for the full agent API reference.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
|
|
3
|
+
export default defineAgent({
|
|
4
|
+
name: "AssemblyAI Support",
|
|
5
|
+
instructions:
|
|
6
|
+
`You are a friendly support agent for AssemblyAI. Help users with questions \
|
|
7
|
+
about AssemblyAI's speech-to-text API, audio intelligence features, and integrations.
|
|
8
|
+
|
|
9
|
+
- Always use vector_search to find relevant documentation before answering.
|
|
10
|
+
- Base your answers strictly on the retrieved documentation — don't guess.
|
|
11
|
+
- If search results aren't relevant to the question, say the docs don't cover that topic \
|
|
12
|
+
and suggest visiting assemblyai.com or contacting support@assemblyai.com.
|
|
13
|
+
- Be concise — this is a voice conversation.
|
|
14
|
+
- When explaining API usage, mention endpoint names and key parameters.
|
|
15
|
+
- If a question is ambiguous, ask the user to clarify which product or feature they mean.`,
|
|
16
|
+
greeting:
|
|
17
|
+
"Hi! I'm the AssemblyAI support assistant. I can help you with questions about our speech-to-text API, audio intelligence features, LLM gateway, and more. What can I help you with?",
|
|
18
|
+
builtinTools: ["vector_search"],
|
|
19
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
|
|
3
|
+
export default defineAgent({
|
|
4
|
+
name: "Aria",
|
|
5
|
+
instructions:
|
|
6
|
+
`You are Aria, a luxury travel concierge. You help customers plan trips,
|
|
7
|
+
find flights and hotels, check weather at destinations, and convert currencies.
|
|
8
|
+
|
|
9
|
+
Rules:
|
|
10
|
+
- Always check weather before recommending activities
|
|
11
|
+
- When discussing costs, convert to the customer's preferred currency
|
|
12
|
+
- Suggest specific restaurants, landmarks, and experiences
|
|
13
|
+
- Be warm and enthusiastic but concise — this is a voice conversation
|
|
14
|
+
- If the customer hasn't specified dates, ask for them before searching flights
|
|
15
|
+
- Use web_search to find current flight and hotel options, then visit_webpage for details
|
|
16
|
+
|
|
17
|
+
API endpoints (use fetch_json):
|
|
18
|
+
- Geocoding: https://geocoding-api.open-meteo.com/v1/search?name={city}&count=1&language=en
|
|
19
|
+
Returns { results: [{ name, country, latitude, longitude }] }
|
|
20
|
+
- Weather forecast: https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,weathercode&timezone=auto&forecast_days=7
|
|
21
|
+
Returns { daily: { time, temperature_2m_max, temperature_2m_min, precipitation_sum, weathercode } }
|
|
22
|
+
Weather codes: 0=Clear, 1=Mainly clear, 2=Partly cloudy, 3=Overcast, 45/48=Fog, 51/53/55=Drizzle, 61/63/65=Rain, 71/73/75=Snow, 80-82=Rain showers, 85/86=Snow showers, 95/96/99=Thunderstorm
|
|
23
|
+
Convert C to F: F = C * 9/5 + 32
|
|
24
|
+
- Currency rates: https://open.er-api.com/v6/latest/{CODE}
|
|
25
|
+
Returns { rates: { USD: 1.0, EUR: 0.85, ... } }`,
|
|
26
|
+
greeting:
|
|
27
|
+
"Hey, I'm Aria, your travel concierge. Try asking me something like, what's the weather in Tokyo this week, or help me plan a long weekend in Barcelona.",
|
|
28
|
+
builtinTools: ["web_search", "visit_webpage", "fetch_json"],
|
|
29
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineAgent } from "@alexkroman1/aai";
|
|
2
|
+
|
|
3
|
+
export default defineAgent({
|
|
4
|
+
name: "Scout",
|
|
5
|
+
instructions:
|
|
6
|
+
`You are Scout, a research assistant who finds answers by searching the web.
|
|
7
|
+
|
|
8
|
+
- Search first. Never guess or rely on memory for factual questions.
|
|
9
|
+
- Use visit_webpage when search snippets aren't detailed enough.
|
|
10
|
+
- For complex questions, search multiple times with different queries.
|
|
11
|
+
- Cite sources by website name.
|
|
12
|
+
- Be concise — this is a voice conversation.
|
|
13
|
+
- If results are unclear or contradictory, say so.`,
|
|
14
|
+
greeting:
|
|
15
|
+
"Hey, I'm Scout. I search the web for answers. Try asking me something like, what happened in tech news today, or who won the last World Cup.",
|
|
16
|
+
builtinTools: ["web_search", "visit_webpage"],
|
|
17
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright 2025 the AAI authors. MIT license.
|
|
2
|
+
import type * as preact from "preact";
|
|
3
|
+
import { useMountConfig } from "../mount_context.ts";
|
|
4
|
+
import { useSession } from "../signals.ts";
|
|
5
|
+
import { ChatView } from "./chat_view.tsx";
|
|
6
|
+
|
|
7
|
+
const AAI_LOGO = `▄▀█ ▄▀█ █\n█▀█ █▀█ █`;
|
|
8
|
+
|
|
9
|
+
export function App(): preact.JSX.Element {
|
|
10
|
+
const { started, start } = useSession();
|
|
11
|
+
const { title } = useMountConfig();
|
|
12
|
+
|
|
13
|
+
if (!started.value) {
|
|
14
|
+
return (
|
|
15
|
+
<div class="flex items-center justify-center h-screen bg-aai-bg font-aai">
|
|
16
|
+
<div class="flex flex-col items-center gap-6 bg-aai-surface border border-aai-border rounded-lg px-12 py-10">
|
|
17
|
+
{title ? (
|
|
18
|
+
<span class="text-lg font-semibold text-aai-primary">{title}</span>
|
|
19
|
+
) : (
|
|
20
|
+
<span class="font-aai-mono text-lg leading-[1.1] font-bold text-aai-primary whitespace-pre">
|
|
21
|
+
{AAI_LOGO}
|
|
22
|
+
</span>
|
|
23
|
+
)}
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
class="h-8 px-4 py-1.5 rounded-aai text-sm font-medium leading-[130%] cursor-pointer bg-aai-surface-hover text-aai-text-secondary border border-aai-border outline-none"
|
|
27
|
+
onClick={start}
|
|
28
|
+
>
|
|
29
|
+
Start
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return <ChatView />;
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright 2025 the AAI authors. MIT license.
|
|
2
|
+
import type * as preact from "preact";
|
|
3
|
+
import { useMountConfig } from "../mount_context.ts";
|
|
4
|
+
import { useSession } from "../signals.ts";
|
|
5
|
+
import { Controls } from "./controls.tsx";
|
|
6
|
+
import { ErrorBanner } from "./error_banner.tsx";
|
|
7
|
+
import { MessageList } from "./message_list.tsx";
|
|
8
|
+
import { StateIndicator } from "./state_indicator.tsx";
|
|
9
|
+
|
|
10
|
+
const AAI_LOGO = `▄▀█ ▄▀█ █\n█▀█ █▀█ █`;
|
|
11
|
+
|
|
12
|
+
export function ChatView(): preact.JSX.Element {
|
|
13
|
+
const { session } = useSession();
|
|
14
|
+
const { title } = useMountConfig();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div class="flex flex-col h-screen max-w-130 mx-auto bg-aai-bg text-aai-text font-aai text-sm">
|
|
18
|
+
{/* Header */}
|
|
19
|
+
<div class="flex items-center gap-3 px-4 py-3 border-b border-aai-border shrink-0">
|
|
20
|
+
{title ? (
|
|
21
|
+
<span class="text-sm font-semibold text-aai-primary">{title}</span>
|
|
22
|
+
) : (
|
|
23
|
+
<span class="font-aai-mono text-[10px] leading-[1.1] font-bold text-aai-primary whitespace-pre">
|
|
24
|
+
{AAI_LOGO}
|
|
25
|
+
</span>
|
|
26
|
+
)}
|
|
27
|
+
<div class="ml-auto">
|
|
28
|
+
<StateIndicator state={session.state} />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<ErrorBanner error={session.error} />
|
|
32
|
+
<MessageList />
|
|
33
|
+
<Controls />
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright 2025 the AAI authors. MIT license.
|
|
2
|
+
import { useSession } from "../signals.ts";
|
|
3
|
+
|
|
4
|
+
export function Controls() {
|
|
5
|
+
const { running, toggle, reset } = useSession();
|
|
6
|
+
|
|
7
|
+
const btnBase =
|
|
8
|
+
"h-8 px-3 py-1.5 rounded-aai text-sm font-medium leading-[130%] cursor-pointer border border-transparent outline-none";
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div class="flex gap-2 px-4 py-3 border-t border-aai-border shrink-0">
|
|
12
|
+
<button
|
|
13
|
+
type="button"
|
|
14
|
+
class={`${btnBase} ${
|
|
15
|
+
running.value
|
|
16
|
+
? "bg-aai-surface-hover text-aai-text-secondary border-aai-border"
|
|
17
|
+
: "bg-aai-surface-hover text-aai-text-secondary border-aai-border"
|
|
18
|
+
}`}
|
|
19
|
+
onClick={toggle}
|
|
20
|
+
>
|
|
21
|
+
{running.value ? "Stop" : "Resume"}
|
|
22
|
+
</button>
|
|
23
|
+
<button
|
|
24
|
+
type="button"
|
|
25
|
+
class={`${btnBase} bg-transparent text-aai-text-secondary border-aai-border`}
|
|
26
|
+
onClick={reset}
|
|
27
|
+
>
|
|
28
|
+
New Conversation
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|