@alexanderolsen/create-deepagent 0.1.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/dist/index.d.ts +2 -0
- package/dist/index.js +661 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/registry/frameworks/deno/.env.example +6 -0
- package/registry/frameworks/deno/README.md +137 -0
- package/registry/frameworks/deno/client/index.html +23 -0
- package/registry/frameworks/deno/client/package.json +30 -0
- package/registry/frameworks/deno/client/public/favicon.ico +0 -0
- package/registry/frameworks/deno/client/src/components/Chat.tsx +124 -0
- package/registry/frameworks/deno/client/src/components/ChatApp.tsx +129 -0
- package/registry/frameworks/deno/client/src/components/Conversation.tsx +91 -0
- package/registry/frameworks/deno/client/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/deno/client/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/deno/client/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/deno/client/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/deno/client/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/deno/client/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/deno/client/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/deno/client/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/deno/client/src/lib/agent-type.ts +4 -0
- package/registry/frameworks/deno/client/src/lib/chat/threads-client.ts +51 -0
- package/registry/frameworks/deno/client/src/main.tsx +11 -0
- package/registry/frameworks/deno/client/src/styles/globals.css +714 -0
- package/registry/frameworks/deno/client/src/vite-env.d.ts +1 -0
- package/registry/frameworks/deno/client/tsconfig.app.json +7 -0
- package/registry/frameworks/deno/client/tsconfig.json +24 -0
- package/registry/frameworks/deno/client/tsconfig.node.json +19 -0
- package/registry/frameworks/deno/client/vite.config.ts +24 -0
- package/registry/frameworks/deno/deno.json +16 -0
- package/registry/frameworks/deno/main.ts +23 -0
- package/registry/frameworks/deno/package.json +14 -0
- package/registry/frameworks/deno/server/agent/index.ts +60 -0
- package/registry/frameworks/deno/server/agent/middleware.ts +24 -0
- package/registry/frameworks/deno/server/agent/tools.ts +64 -0
- package/registry/frameworks/deno/server/registry.ts +40 -0
- package/registry/frameworks/deno/server/routes.ts +114 -0
- package/registry/frameworks/deno/server/serialize.ts +30 -0
- package/registry/frameworks/deno/server/session.ts +210 -0
- package/registry/frameworks/deno/server/threads.ts +404 -0
- package/registry/frameworks/deno.ts +17 -0
- package/registry/frameworks/hono/.env.example +6 -0
- package/registry/frameworks/hono/README.md +186 -0
- package/registry/frameworks/hono/index.html +22 -0
- package/registry/frameworks/hono/package.json +42 -0
- package/registry/frameworks/hono/src/components/Chat.tsx +124 -0
- package/registry/frameworks/hono/src/components/ChatApp.tsx +129 -0
- package/registry/frameworks/hono/src/components/Conversation.tsx +90 -0
- package/registry/frameworks/hono/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/hono/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/hono/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/hono/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/hono/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/hono/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/hono/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/hono/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/hono/src/lib/agent/types.ts +4 -0
- package/registry/frameworks/hono/src/lib/chat/threads-client.ts +57 -0
- package/registry/frameworks/hono/src/main.tsx +11 -0
- package/registry/frameworks/hono/src/styles/globals.css +714 -0
- package/registry/frameworks/hono/src/vite-env.d.ts +1 -0
- package/registry/frameworks/hono/tsconfig.app.json +22 -0
- package/registry/frameworks/hono/tsconfig.json +7 -0
- package/registry/frameworks/hono/tsconfig.worker.json +18 -0
- package/registry/frameworks/hono/vite.config.ts +16 -0
- package/registry/frameworks/hono/worker/agent/index.ts +53 -0
- package/registry/frameworks/hono/worker/agent/middleware.ts +20 -0
- package/registry/frameworks/hono/worker/agent/tools.ts +55 -0
- package/registry/frameworks/hono/worker/durable-objects/thread-session.ts +159 -0
- package/registry/frameworks/hono/worker/env.d.ts +17 -0
- package/registry/frameworks/hono/worker/index.ts +140 -0
- package/registry/frameworks/hono/worker/server/registry.ts +39 -0
- package/registry/frameworks/hono/worker/server/runs.ts +82 -0
- package/registry/frameworks/hono/worker/server/serialize.ts +30 -0
- package/registry/frameworks/hono/worker/server/threads.ts +404 -0
- package/registry/frameworks/hono/worker/tsconfig.json +4 -0
- package/registry/frameworks/hono/wrangler.jsonc +28 -0
- package/registry/frameworks/hono.ts +35 -0
- package/registry/frameworks/next/.env.example +6 -0
- package/registry/frameworks/next/README.md +173 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/commands/route.ts +21 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/history/route.ts +35 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/route.ts +13 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/state/route.ts +51 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/stream/route.ts +30 -0
- package/registry/frameworks/next/app/api/threads/route.ts +11 -0
- package/registry/frameworks/next/app/favicon.ico +0 -0
- package/registry/frameworks/next/app/globals.css +712 -0
- package/registry/frameworks/next/app/layout.tsx +34 -0
- package/registry/frameworks/next/app/page.tsx +5 -0
- package/registry/frameworks/next/components/Chat.tsx +124 -0
- package/registry/frameworks/next/components/ChatApp.tsx +129 -0
- package/registry/frameworks/next/components/Conversation.tsx +90 -0
- package/registry/frameworks/next/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/next/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/next/components/MessageThread.tsx +135 -0
- package/registry/frameworks/next/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/next/components/Subagents.tsx +120 -0
- package/registry/frameworks/next/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/next/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/next/components/ToolCall.tsx +89 -0
- package/registry/frameworks/next/eslint.config.mjs +18 -0
- package/registry/frameworks/next/lib/agent/index.ts +95 -0
- package/registry/frameworks/next/lib/agent/middleware.ts +40 -0
- package/registry/frameworks/next/lib/agent/tools.ts +66 -0
- package/registry/frameworks/next/lib/chat/threads-client.ts +57 -0
- package/registry/frameworks/next/lib/server/registry.ts +57 -0
- package/registry/frameworks/next/lib/server/serialize.ts +32 -0
- package/registry/frameworks/next/lib/server/session.ts +212 -0
- package/registry/frameworks/next/lib/server/threads.ts +406 -0
- package/registry/frameworks/next/next.config.ts +7 -0
- package/registry/frameworks/next/package.json +37 -0
- package/registry/frameworks/next/postcss.config.mjs +7 -0
- package/registry/frameworks/next/public/file.svg +1 -0
- package/registry/frameworks/next/public/globe.svg +1 -0
- package/registry/frameworks/next/public/next.svg +1 -0
- package/registry/frameworks/next/public/vercel.svg +1 -0
- package/registry/frameworks/next/public/window.svg +1 -0
- package/registry/frameworks/next/tsconfig.json +34 -0
- package/registry/frameworks/next.ts +17 -0
- package/registry/frameworks/nuxt/.env.example +3 -0
- package/registry/frameworks/nuxt/README.md +133 -0
- package/registry/frameworks/nuxt/app/app.vue +26 -0
- package/registry/frameworks/nuxt/app/assets/css/main.css +707 -0
- package/registry/frameworks/nuxt/app/components/Chat.vue +105 -0
- package/registry/frameworks/nuxt/app/components/ChatApp.vue +89 -0
- package/registry/frameworks/nuxt/app/components/ChatThread.vue +27 -0
- package/registry/frameworks/nuxt/app/components/MessageBubble.vue +60 -0
- package/registry/frameworks/nuxt/app/components/MessageBubbles.vue +213 -0
- package/registry/frameworks/nuxt/app/components/MessageList.vue +51 -0
- package/registry/frameworks/nuxt/app/components/MessageReasoning.vue +53 -0
- package/registry/frameworks/nuxt/app/components/StreamingIndicator.vue +9 -0
- package/registry/frameworks/nuxt/app/components/SubagentDetail.vue +51 -0
- package/registry/frameworks/nuxt/app/components/SubagentList.vue +49 -0
- package/registry/frameworks/nuxt/app/components/ThemeToggle.vue +43 -0
- package/registry/frameworks/nuxt/app/components/ThreadHistory.vue +65 -0
- package/registry/frameworks/nuxt/app/components/ToolCall.vue +81 -0
- package/registry/frameworks/nuxt/app/components/TypingDots.vue +14 -0
- package/registry/frameworks/nuxt/app/composables/useTheme.ts +14 -0
- package/registry/frameworks/nuxt/app/utils/streaming.ts +44 -0
- package/registry/frameworks/nuxt/app/utils/threads.ts +57 -0
- package/registry/frameworks/nuxt/nuxt.config.ts +6 -0
- package/registry/frameworks/nuxt/package.json +28 -0
- package/registry/frameworks/nuxt/public/favicon.ico +0 -0
- package/registry/frameworks/nuxt/public/robots.txt +2 -0
- package/registry/frameworks/nuxt/server/agent/index.ts +89 -0
- package/registry/frameworks/nuxt/server/agent/middleware.ts +38 -0
- package/registry/frameworks/nuxt/server/agent/tools.ts +66 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/commands.post.ts +16 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/history.post.ts +37 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/index.delete.ts +12 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.get.ts +22 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.post.ts +32 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/stream.post.ts +24 -0
- package/registry/frameworks/nuxt/server/api/threads/index.get.ts +13 -0
- package/registry/frameworks/nuxt/server/utils/runtime.ts +42 -0
- package/registry/frameworks/nuxt/server/utils/serialize.ts +30 -0
- package/registry/frameworks/nuxt/server/utils/session.ts +210 -0
- package/registry/frameworks/nuxt/server/utils/threads.ts +404 -0
- package/registry/frameworks/nuxt/tsconfig.json +18 -0
- package/registry/frameworks/nuxt.ts +17 -0
- package/registry/frameworks/vite/.env.example +20 -0
- package/registry/frameworks/vite/README.md +149 -0
- package/registry/frameworks/vite/agent/index.ts +59 -0
- package/registry/frameworks/vite/agent/middleware.ts +24 -0
- package/registry/frameworks/vite/agent/tools.ts +64 -0
- package/registry/frameworks/vite/index.html +23 -0
- package/registry/frameworks/vite/langgraph.json +16 -0
- package/registry/frameworks/vite/package.json +39 -0
- package/registry/frameworks/vite/public/favicon.ico +0 -0
- package/registry/frameworks/vite/scripts/vite-langgraph-proxy.ts +34 -0
- package/registry/frameworks/vite/src/components/Chat.tsx +124 -0
- package/registry/frameworks/vite/src/components/ChatApp.tsx +122 -0
- package/registry/frameworks/vite/src/components/Conversation.tsx +91 -0
- package/registry/frameworks/vite/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/vite/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/vite/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/vite/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/vite/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/vite/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/vite/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/vite/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/vite/src/lib/agent-type.ts +4 -0
- package/registry/frameworks/vite/src/lib/chat/threads-client.ts +114 -0
- package/registry/frameworks/vite/src/main.tsx +11 -0
- package/registry/frameworks/vite/src/styles/globals.css +714 -0
- package/registry/frameworks/vite/src/vite-env.d.ts +11 -0
- package/registry/frameworks/vite/tsconfig.app.json +24 -0
- package/registry/frameworks/vite/tsconfig.json +7 -0
- package/registry/frameworks/vite/tsconfig.node.json +21 -0
- package/registry/frameworks/vite/vercel.json +3 -0
- package/registry/frameworks/vite/vite.config.ts +24 -0
- package/registry/frameworks/vite.ts +17 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from "vue";
|
|
3
|
+
import { HumanMessage } from "@langchain/core/messages";
|
|
4
|
+
import { useStreamContext } from "@langchain/vue";
|
|
5
|
+
|
|
6
|
+
import MessageList from "./MessageList.vue";
|
|
7
|
+
import SubagentDetail from "./SubagentDetail.vue";
|
|
8
|
+
|
|
9
|
+
const EXAMPLE_PROMPT =
|
|
10
|
+
"Research LangGraph streaming, and separately calculate 42 * 17.";
|
|
11
|
+
|
|
12
|
+
const emit = defineEmits<{ runSettled: [] }>();
|
|
13
|
+
|
|
14
|
+
const stream = useStreamContext();
|
|
15
|
+
|
|
16
|
+
// Refresh the sidebar whenever a run finishes (titles derive from the first
|
|
17
|
+
// message; order from the latest checkpoint, both owned by the server).
|
|
18
|
+
watch(
|
|
19
|
+
() => stream.isLoading.value,
|
|
20
|
+
(loading) => {
|
|
21
|
+
if (!loading) emit("runSettled");
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const content = ref(EXAMPLE_PROMPT);
|
|
26
|
+
const openSubagentId = ref<string | null>(null);
|
|
27
|
+
const textareaRef = ref<HTMLTextAreaElement | null>(null);
|
|
28
|
+
|
|
29
|
+
const subagents = computed(() => [...stream.subagents.value.values()]);
|
|
30
|
+
const openSubagent = computed(() =>
|
|
31
|
+
openSubagentId.value
|
|
32
|
+
? subagents.value.find((snapshot) => snapshot.id === openSubagentId.value)
|
|
33
|
+
: undefined,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
function autoGrow() {
|
|
37
|
+
const node = textareaRef.value;
|
|
38
|
+
if (!node) return;
|
|
39
|
+
node.style.height = "auto";
|
|
40
|
+
node.style.height = `${Math.min(node.scrollHeight, 200)}px`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function handleSubmit() {
|
|
44
|
+
const nextContent = content.value.trim();
|
|
45
|
+
if (nextContent.length === 0 || stream.isLoading.value) return;
|
|
46
|
+
|
|
47
|
+
content.value = "";
|
|
48
|
+
if (textareaRef.value) textareaRef.value.style.height = "auto";
|
|
49
|
+
void stream.submit({ messages: [new HumanMessage(nextContent)] });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
53
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
handleSubmit();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<!-- Subagent detail view: breadcrumb + that subagent's chat (no composer). -->
|
|
62
|
+
<main v-if="openSubagent" class="chat-main">
|
|
63
|
+
<nav aria-label="Breadcrumb" class="breadcrumb">
|
|
64
|
+
<button class="crumb-link" type="button" @click="openSubagentId = null">
|
|
65
|
+
Main chat
|
|
66
|
+
</button>
|
|
67
|
+
<span class="crumb-sep">/</span>
|
|
68
|
+
<span class="crumb-current">{{ openSubagent.name }}</span>
|
|
69
|
+
</nav>
|
|
70
|
+
<div class="conversation">
|
|
71
|
+
<div class="conversation-inner">
|
|
72
|
+
<SubagentDetail :snapshot="openSubagent" />
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</main>
|
|
76
|
+
|
|
77
|
+
<!-- Main view: messages + subagent chips, composer pinned at the bottom. -->
|
|
78
|
+
<main v-else class="chat-main">
|
|
79
|
+
<div class="conversation">
|
|
80
|
+
<div class="conversation-inner">
|
|
81
|
+
<MessageList @open-subagent="openSubagentId = $event" />
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div class="composer-bar">
|
|
86
|
+
<form class="composer" @submit.prevent="handleSubmit">
|
|
87
|
+
<textarea
|
|
88
|
+
ref="textareaRef"
|
|
89
|
+
v-model="content"
|
|
90
|
+
aria-label="Message"
|
|
91
|
+
placeholder="Ask for research, a calculation, or both..."
|
|
92
|
+
rows="1"
|
|
93
|
+
@input="autoGrow"
|
|
94
|
+
@keydown="handleKeydown"
|
|
95
|
+
/>
|
|
96
|
+
<button
|
|
97
|
+
:disabled="content.trim() === '' || stream.isLoading.value"
|
|
98
|
+
type="submit"
|
|
99
|
+
>
|
|
100
|
+
Send
|
|
101
|
+
</button>
|
|
102
|
+
</form>
|
|
103
|
+
</div>
|
|
104
|
+
</main>
|
|
105
|
+
</template>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, ref } from "vue";
|
|
3
|
+
|
|
4
|
+
import { useTheme } from "~/composables/useTheme";
|
|
5
|
+
import {
|
|
6
|
+
type ThreadSummary,
|
|
7
|
+
createThread,
|
|
8
|
+
deleteThread,
|
|
9
|
+
fetchThreads,
|
|
10
|
+
} from "~/utils/threads";
|
|
11
|
+
import ChatThread from "./ChatThread.vue";
|
|
12
|
+
import ThemeToggle from "./ThemeToggle.vue";
|
|
13
|
+
import ThreadHistory from "./ThreadHistory.vue";
|
|
14
|
+
|
|
15
|
+
const { theme } = useTheme();
|
|
16
|
+
|
|
17
|
+
const mounted = ref(false);
|
|
18
|
+
const threads = ref<ThreadSummary[]>([]);
|
|
19
|
+
const threadId = ref("");
|
|
20
|
+
|
|
21
|
+
async function refreshThreads() {
|
|
22
|
+
threads.value = await fetchThreads();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// On mount, load threads from the server (single source of truth). If none
|
|
26
|
+
// exist yet, create one.
|
|
27
|
+
onMounted(async () => {
|
|
28
|
+
const list = await fetchThreads();
|
|
29
|
+
if (list.length > 0) {
|
|
30
|
+
threads.value = list;
|
|
31
|
+
threadId.value = list[0]!.id;
|
|
32
|
+
} else {
|
|
33
|
+
const id = await createThread();
|
|
34
|
+
threads.value = await fetchThreads();
|
|
35
|
+
threadId.value = id;
|
|
36
|
+
}
|
|
37
|
+
mounted.value = true;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function handleSelect(id: string) {
|
|
41
|
+
if (id !== threadId.value) threadId.value = id;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function handleCreate() {
|
|
45
|
+
const id = await createThread();
|
|
46
|
+
await refreshThreads();
|
|
47
|
+
threadId.value = id;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function handleDelete(id: string) {
|
|
51
|
+
await deleteThread(id);
|
|
52
|
+
const list = await fetchThreads();
|
|
53
|
+
threads.value = list;
|
|
54
|
+
if (id !== threadId.value) return;
|
|
55
|
+
if (list.length > 0) {
|
|
56
|
+
threadId.value = list[0]!.id;
|
|
57
|
+
} else {
|
|
58
|
+
const freshId = await createThread();
|
|
59
|
+
threads.value = await fetchThreads();
|
|
60
|
+
threadId.value = freshId;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<template>
|
|
66
|
+
<div :class="['app-shell', { light: theme === 'light' }]">
|
|
67
|
+
<template v-if="!mounted || !threadId">
|
|
68
|
+
<div class="empty-state center">Preparing chat…</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<template v-else>
|
|
72
|
+
<ThemeToggle />
|
|
73
|
+
|
|
74
|
+
<ThreadHistory
|
|
75
|
+
:active-thread-id="threadId"
|
|
76
|
+
:threads="threads"
|
|
77
|
+
@create="handleCreate"
|
|
78
|
+
@delete="handleDelete"
|
|
79
|
+
@select="handleSelect"
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
<ChatThread
|
|
83
|
+
:key="threadId"
|
|
84
|
+
:thread-id="threadId"
|
|
85
|
+
@run-settled="refreshThreads"
|
|
86
|
+
/>
|
|
87
|
+
</template>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { HttpAgentServerAdapter, provideStream } from "@langchain/vue";
|
|
3
|
+
|
|
4
|
+
import { getApiUrl } from "~/utils/threads";
|
|
5
|
+
import Chat from "./Chat.vue";
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{ threadId: string }>();
|
|
8
|
+
const emit = defineEmits<{ runSettled: [] }>();
|
|
9
|
+
|
|
10
|
+
// The component is keyed by `threadId` in the parent, so the transport is built
|
|
11
|
+
// once per thread. Provide the stream so every descendant reads it via
|
|
12
|
+
// `useStreamContext()`.
|
|
13
|
+
const transport = new HttpAgentServerAdapter({
|
|
14
|
+
apiUrl: getApiUrl(),
|
|
15
|
+
threadId: props.threadId,
|
|
16
|
+
paths: {
|
|
17
|
+
commands: `/threads/${props.threadId}/commands`,
|
|
18
|
+
stream: `/threads/${props.threadId}/stream`,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
provideStream({ transport, threadId: props.threadId });
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<Chat @run-settled="emit('runSettled')" />
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { AIMessage, type BaseMessage } from "@langchain/core/messages";
|
|
4
|
+
|
|
5
|
+
type ToolCallLike = {
|
|
6
|
+
name: string;
|
|
7
|
+
args?: Record<string, unknown>;
|
|
8
|
+
id?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
message: BaseMessage;
|
|
13
|
+
/** Override which tool calls are shown (e.g. to hide `task` calls). */
|
|
14
|
+
toolCalls?: ToolCallLike[];
|
|
15
|
+
}>();
|
|
16
|
+
|
|
17
|
+
const calls = computed<ToolCallLike[]>(() => {
|
|
18
|
+
if (props.toolCalls) return props.toolCalls;
|
|
19
|
+
return AIMessage.isInstance(props.message)
|
|
20
|
+
? (props.message.tool_calls ?? [])
|
|
21
|
+
: [];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function messageLabel(message: BaseMessage) {
|
|
25
|
+
if (message.type === "human") return "You";
|
|
26
|
+
if (message.type === "tool") return `Tool · ${message.name ?? "result"}`;
|
|
27
|
+
if (message.type === "ai") return "Assistant";
|
|
28
|
+
return message.type;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatToolArgs(args: Record<string, unknown>) {
|
|
32
|
+
const entries = Object.entries(args);
|
|
33
|
+
if (entries.length === 0) return "";
|
|
34
|
+
if (entries.length === 1) return String(entries[0]?.[1] ?? "");
|
|
35
|
+
return JSON.stringify(args);
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div
|
|
41
|
+
:class="[
|
|
42
|
+
'message',
|
|
43
|
+
{ user: message.type === 'human', tool: message.type === 'tool' },
|
|
44
|
+
]"
|
|
45
|
+
>
|
|
46
|
+
<span>{{ messageLabel(message) }}</span>
|
|
47
|
+
<ul v-if="calls.length > 0" class="tool-call-list">
|
|
48
|
+
<li
|
|
49
|
+
v-for="(toolCall, toolIndex) in calls"
|
|
50
|
+
:key="toolCall.id ?? toolIndex"
|
|
51
|
+
>
|
|
52
|
+
<strong>{{ toolCall.name }}</strong>
|
|
53
|
+
<template v-if="formatToolArgs(toolCall.args ?? {})">
|
|
54
|
+
({{ formatToolArgs(toolCall.args ?? {}) }})
|
|
55
|
+
</template>
|
|
56
|
+
</li>
|
|
57
|
+
</ul>
|
|
58
|
+
<p v-if="message.text">{{ message.text }}</p>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { AIMessage, type BaseMessage } from "@langchain/core/messages";
|
|
4
|
+
import type { SubagentDiscoverySnapshot } from "@langchain/vue";
|
|
5
|
+
|
|
6
|
+
import { getReasoningText } from "~/utils/streaming";
|
|
7
|
+
import MessageBubble from "./MessageBubble.vue";
|
|
8
|
+
import MessageReasoning from "./MessageReasoning.vue";
|
|
9
|
+
import SubagentList, { type SubagentCard } from "./SubagentList.vue";
|
|
10
|
+
import ToolCall, { type ToolCallView } from "./ToolCall.vue";
|
|
11
|
+
|
|
12
|
+
type ToolCallLike = {
|
|
13
|
+
name: string;
|
|
14
|
+
args?: Record<string, unknown>;
|
|
15
|
+
id?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Deep agents delegate to subagents through the built-in `task` tool. */
|
|
19
|
+
const TASK_TOOL = "task";
|
|
20
|
+
|
|
21
|
+
const EMPTY_TOOL_CALL: ToolCallView = {
|
|
22
|
+
id: "",
|
|
23
|
+
name: "",
|
|
24
|
+
args: {},
|
|
25
|
+
status: "complete",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const props = defineProps<{
|
|
29
|
+
messages: BaseMessage[];
|
|
30
|
+
/** Whether the surrounding run is streaming — drives reasoning/tool-call state. */
|
|
31
|
+
isLoading?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Root-stream subagent snapshots. When provided ("root mode"), `task` tool
|
|
34
|
+
* calls render as inline subagent cards at the point of delegation instead of
|
|
35
|
+
* tool-call chips. In both modes, regular tool calls render as collapsible
|
|
36
|
+
* {@link ToolCall} chips with their result folded in, and standalone tool
|
|
37
|
+
* result messages are hidden.
|
|
38
|
+
*/
|
|
39
|
+
subagents?: SubagentDiscoverySnapshot[];
|
|
40
|
+
}>();
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits<{ openSubagent: [id: string] }>();
|
|
43
|
+
|
|
44
|
+
const rootMode = computed(() => props.subagents != null);
|
|
45
|
+
|
|
46
|
+
const subagentsById = computed(() => {
|
|
47
|
+
const map = new Map<string, SubagentDiscoverySnapshot>();
|
|
48
|
+
for (const snapshot of props.subagents ?? []) map.set(snapshot.id, snapshot);
|
|
49
|
+
return map;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
type Item = {
|
|
53
|
+
key: string;
|
|
54
|
+
kind: "reasoning" | "bubble" | "subagents" | "toolcall";
|
|
55
|
+
message: BaseMessage;
|
|
56
|
+
reasoning: string;
|
|
57
|
+
active: boolean;
|
|
58
|
+
cards: SubagentCard[];
|
|
59
|
+
toolCall: ToolCallView;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function hasToolCalls(message: BaseMessage): boolean {
|
|
63
|
+
return AIMessage.isInstance(message) && (message.tool_calls?.length ?? 0) > 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildCards(tasks: ToolCallLike[]): SubagentCard[] {
|
|
67
|
+
return tasks.map((call, index) => {
|
|
68
|
+
const snapshot = call.id ? subagentsById.value.get(call.id) : undefined;
|
|
69
|
+
const args = (call.args ?? {}) as Record<string, unknown>;
|
|
70
|
+
return {
|
|
71
|
+
id: call.id ?? `task-${index}`,
|
|
72
|
+
name: snapshot?.name ?? String(args.subagent_type ?? "subagent"),
|
|
73
|
+
task:
|
|
74
|
+
snapshot?.taskInput ??
|
|
75
|
+
(typeof args.description === "string" ? args.description : undefined),
|
|
76
|
+
status: snapshot?.status ?? "running",
|
|
77
|
+
openable: snapshot != null,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Each tool call is folded together with its result message (matched by
|
|
83
|
+
// `tool_call_id`) into a single collapsible chip; reasoning renders standalone
|
|
84
|
+
// before the answer; and `task` delegations render as subagent cards (root
|
|
85
|
+
// mode). Standalone tool result messages are hidden.
|
|
86
|
+
const items = computed<Item[]>(() => {
|
|
87
|
+
const result: Item[] = [];
|
|
88
|
+
|
|
89
|
+
const resultsByCallId = new Map<string, BaseMessage>();
|
|
90
|
+
for (const message of props.messages) {
|
|
91
|
+
if (message.type !== "tool") continue;
|
|
92
|
+
const id = (message as { tool_call_id?: unknown }).tool_call_id;
|
|
93
|
+
if (typeof id === "string") resultsByCallId.set(id, message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
props.messages.forEach((message, index) => {
|
|
97
|
+
if (message.type === "tool") return; // folded into its tool-call chip
|
|
98
|
+
|
|
99
|
+
if (AIMessage.isInstance(message)) {
|
|
100
|
+
const calls = (message.tool_calls ?? []) as ToolCallLike[];
|
|
101
|
+
const tasks = rootMode.value
|
|
102
|
+
? calls.filter((call) => call.name === TASK_TOOL)
|
|
103
|
+
: [];
|
|
104
|
+
const chipCalls = rootMode.value
|
|
105
|
+
? calls.filter((call) => call.name !== TASK_TOOL)
|
|
106
|
+
: calls;
|
|
107
|
+
|
|
108
|
+
const reasoning = getReasoningText(message);
|
|
109
|
+
if (reasoning) {
|
|
110
|
+
const active =
|
|
111
|
+
Boolean(props.isLoading) &&
|
|
112
|
+
index === props.messages.length - 1 &&
|
|
113
|
+
!message.text?.trim() &&
|
|
114
|
+
!hasToolCalls(message);
|
|
115
|
+
result.push({
|
|
116
|
+
key: `reason-${message.id ?? index}`,
|
|
117
|
+
kind: "reasoning",
|
|
118
|
+
message,
|
|
119
|
+
reasoning,
|
|
120
|
+
active,
|
|
121
|
+
cards: [],
|
|
122
|
+
toolCall: EMPTY_TOOL_CALL,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (message.text?.trim()) {
|
|
127
|
+
result.push({
|
|
128
|
+
key: message.id ?? `m-${index}`,
|
|
129
|
+
kind: "bubble",
|
|
130
|
+
message,
|
|
131
|
+
reasoning: "",
|
|
132
|
+
active: false,
|
|
133
|
+
cards: [],
|
|
134
|
+
toolCall: EMPTY_TOOL_CALL,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (tasks.length > 0) {
|
|
139
|
+
result.push({
|
|
140
|
+
key: `task-${message.id ?? index}`,
|
|
141
|
+
kind: "subagents",
|
|
142
|
+
message,
|
|
143
|
+
reasoning: "",
|
|
144
|
+
active: false,
|
|
145
|
+
cards: buildCards(tasks),
|
|
146
|
+
toolCall: EMPTY_TOOL_CALL,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
chipCalls.forEach((call, callIndex) => {
|
|
151
|
+
const resultMessage = call.id
|
|
152
|
+
? resultsByCallId.get(call.id)
|
|
153
|
+
: undefined;
|
|
154
|
+
const errored =
|
|
155
|
+
(resultMessage as { status?: string } | undefined)?.status ===
|
|
156
|
+
"error";
|
|
157
|
+
const view: ToolCallView = {
|
|
158
|
+
id: call.id ?? `${index}-${callIndex}`,
|
|
159
|
+
name: call.name,
|
|
160
|
+
args: call.args ?? {},
|
|
161
|
+
output: resultMessage?.text,
|
|
162
|
+
status: resultMessage
|
|
163
|
+
? errored
|
|
164
|
+
? "error"
|
|
165
|
+
: "complete"
|
|
166
|
+
: props.isLoading
|
|
167
|
+
? "running"
|
|
168
|
+
: "complete",
|
|
169
|
+
};
|
|
170
|
+
result.push({
|
|
171
|
+
key: `tc-${view.id}`,
|
|
172
|
+
kind: "toolcall",
|
|
173
|
+
message,
|
|
174
|
+
reasoning: "",
|
|
175
|
+
active: false,
|
|
176
|
+
cards: [],
|
|
177
|
+
toolCall: view,
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
result.push({
|
|
184
|
+
key: message.id ?? `m-${index}`,
|
|
185
|
+
kind: "bubble",
|
|
186
|
+
message,
|
|
187
|
+
reasoning: "",
|
|
188
|
+
active: false,
|
|
189
|
+
cards: [],
|
|
190
|
+
toolCall: EMPTY_TOOL_CALL,
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
return result;
|
|
195
|
+
});
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<template>
|
|
199
|
+
<template v-for="item in items" :key="item.key">
|
|
200
|
+
<MessageReasoning
|
|
201
|
+
v-if="item.kind === 'reasoning'"
|
|
202
|
+
:active="item.active"
|
|
203
|
+
:reasoning="item.reasoning"
|
|
204
|
+
/>
|
|
205
|
+
<SubagentList
|
|
206
|
+
v-else-if="item.kind === 'subagents'"
|
|
207
|
+
:cards="item.cards"
|
|
208
|
+
@open="emit('openSubagent', $event)"
|
|
209
|
+
/>
|
|
210
|
+
<ToolCall v-else-if="item.kind === 'toolcall'" :call="item.toolCall" />
|
|
211
|
+
<MessageBubble v-else :message="item.message" :tool-calls="[]" />
|
|
212
|
+
</template>
|
|
213
|
+
</template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
4
|
+
import { useStreamContext } from "@langchain/vue";
|
|
5
|
+
|
|
6
|
+
import { shouldShowTypingIndicator } from "~/utils/streaming";
|
|
7
|
+
import MessageBubbles from "./MessageBubbles.vue";
|
|
8
|
+
import StreamingIndicator from "./StreamingIndicator.vue";
|
|
9
|
+
|
|
10
|
+
const emit = defineEmits<{ openSubagent: [id: string] }>();
|
|
11
|
+
|
|
12
|
+
const stream = useStreamContext();
|
|
13
|
+
|
|
14
|
+
const messages = computed(() =>
|
|
15
|
+
stream.messages.value.filter(
|
|
16
|
+
(message): message is BaseMessage => message != null,
|
|
17
|
+
),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const subagents = computed(() => [...stream.subagents.value.values()]);
|
|
21
|
+
|
|
22
|
+
const showTypingIndicator = computed(() =>
|
|
23
|
+
shouldShowTypingIndicator(messages.value, stream.isLoading.value),
|
|
24
|
+
);
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div v-if="messages.length === 0 && !stream.error.value" class="empty-state">
|
|
29
|
+
Ask a question below. The coordinator will delegate to its subagents and
|
|
30
|
+
stream tokens, tool calls, and results.
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<MessageBubbles
|
|
34
|
+
:is-loading="stream.isLoading.value"
|
|
35
|
+
:messages="messages"
|
|
36
|
+
:subagents="subagents"
|
|
37
|
+
@open-subagent="emit('openSubagent', $event)"
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
<StreamingIndicator v-if="showTypingIndicator" />
|
|
41
|
+
|
|
42
|
+
<div
|
|
43
|
+
v-if="
|
|
44
|
+
messages.length === 0 && !stream.isLoading.value && stream.error.value
|
|
45
|
+
"
|
|
46
|
+
class="error"
|
|
47
|
+
>
|
|
48
|
+
Could not reach the agent API. Make sure the dev server is running and
|
|
49
|
+
<code>OPENAI_API_KEY</code> is set, then try again.
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, watch } from "vue";
|
|
3
|
+
|
|
4
|
+
import TypingDots from "./TypingDots.vue";
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ reasoning: string; active: boolean }>();
|
|
7
|
+
|
|
8
|
+
// Follow the streaming state: expand on start, collapse on finish. A manual
|
|
9
|
+
// toggle in between is preserved until `active` flips again.
|
|
10
|
+
const open = ref(props.active);
|
|
11
|
+
watch(
|
|
12
|
+
() => props.active,
|
|
13
|
+
(value) => {
|
|
14
|
+
open.value = value;
|
|
15
|
+
},
|
|
16
|
+
);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div :class="['reasoning', { open }]">
|
|
21
|
+
<button
|
|
22
|
+
:aria-expanded="open"
|
|
23
|
+
class="reasoning-toggle"
|
|
24
|
+
type="button"
|
|
25
|
+
@click="open = !open"
|
|
26
|
+
>
|
|
27
|
+
<span aria-hidden class="reasoning-caret">▸</span>
|
|
28
|
+
<span aria-hidden class="reasoning-icon">
|
|
29
|
+
<svg
|
|
30
|
+
fill="none"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
stroke-linecap="round"
|
|
33
|
+
stroke-linejoin="round"
|
|
34
|
+
stroke-width="1.6"
|
|
35
|
+
viewBox="0 0 24 24"
|
|
36
|
+
>
|
|
37
|
+
<path
|
|
38
|
+
d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"
|
|
39
|
+
/>
|
|
40
|
+
<path
|
|
41
|
+
d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"
|
|
42
|
+
/>
|
|
43
|
+
<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4" />
|
|
44
|
+
<path d="M6 18a4 4 0 0 1-1.967-.516" />
|
|
45
|
+
<path d="M19.967 17.484A4 4 0 0 1 18 18" />
|
|
46
|
+
</svg>
|
|
47
|
+
</span>
|
|
48
|
+
<span class="reasoning-label">Thinking</span>
|
|
49
|
+
<TypingDots v-if="active" variant="inline" class="reasoning-dots" />
|
|
50
|
+
</button>
|
|
51
|
+
<p v-if="open" class="reasoning-text">{{ reasoning }}</p>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, toRef } from "vue";
|
|
3
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
4
|
+
import {
|
|
5
|
+
useMessages,
|
|
6
|
+
useStreamContext,
|
|
7
|
+
type SubagentDiscoverySnapshot,
|
|
8
|
+
} from "@langchain/vue";
|
|
9
|
+
|
|
10
|
+
import MessageBubbles from "./MessageBubbles.vue";
|
|
11
|
+
import StreamingIndicator from "./StreamingIndicator.vue";
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{ snapshot: SubagentDiscoverySnapshot }>();
|
|
14
|
+
|
|
15
|
+
const stream = useStreamContext();
|
|
16
|
+
|
|
17
|
+
// `useMessages` is scoped to the subagent's namespace, so its tokens, tool
|
|
18
|
+
// calls, and results stream independently from the root conversation.
|
|
19
|
+
const messages = useMessages(stream, toRef(props, "snapshot"));
|
|
20
|
+
|
|
21
|
+
function omitTaskHumanMessage(
|
|
22
|
+
thread: BaseMessage[],
|
|
23
|
+
taskInput?: string,
|
|
24
|
+
): BaseMessage[] {
|
|
25
|
+
const task = taskInput?.trim();
|
|
26
|
+
if (!task) return thread;
|
|
27
|
+
return thread.filter(
|
|
28
|
+
(message) => message.type !== "human" || message.text?.trim() !== task,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const visibleMessages = computed(() =>
|
|
33
|
+
omitTaskHumanMessage(messages.value, props.snapshot.taskInput),
|
|
34
|
+
);
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<div v-if="snapshot.taskInput" class="subagent-prompt">
|
|
39
|
+
<span>Task</span>
|
|
40
|
+
<p>{{ snapshot.taskInput }}</p>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<MessageBubbles
|
|
44
|
+
:is-loading="snapshot.status === 'running'"
|
|
45
|
+
:messages="visibleMessages"
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<StreamingIndicator
|
|
49
|
+
v-if="snapshot.status === 'running' && visibleMessages.length === 0"
|
|
50
|
+
/>
|
|
51
|
+
</template>
|