@langgraph-js/pure-graph 1.0.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/README.md +16 -0
- package/dist/adapter/hono/assistants.d.ts +3 -0
- package/dist/adapter/hono/assistants.js +27 -0
- package/dist/adapter/hono/endpoint.d.ts +1 -0
- package/dist/adapter/hono/endpoint.js +3 -0
- package/dist/adapter/hono/index.d.ts +3 -0
- package/dist/adapter/hono/index.js +11 -0
- package/dist/adapter/hono/runs.d.ts +3 -0
- package/dist/adapter/hono/runs.js +71 -0
- package/dist/adapter/hono/threads.d.ts +3 -0
- package/dist/adapter/hono/threads.js +71 -0
- package/dist/adapter/hono/zod.d.ts +203 -0
- package/dist/adapter/hono/zod.js +43 -0
- package/dist/createEndpoint.d.ts +5 -0
- package/dist/createEndpoint.js +77 -0
- package/dist/global.d.ts +4 -0
- package/dist/global.js +5 -0
- package/dist/graph/stream.d.ts +39 -0
- package/dist/graph/stream.js +187 -0
- package/dist/graph/stringify.d.ts +1 -0
- package/dist/graph/stringify.js +214 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/queue/JsonPlusSerializer.d.ts +7 -0
- package/dist/queue/JsonPlusSerializer.js +138 -0
- package/dist/queue/event_message.d.ts +15 -0
- package/dist/queue/event_message.js +27 -0
- package/dist/queue/stream_queue.d.ts +161 -0
- package/dist/queue/stream_queue.js +175 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/memory/checkpoint.d.ts +2 -0
- package/dist/storage/memory/checkpoint.js +2 -0
- package/dist/storage/memory/queue.d.ts +17 -0
- package/dist/storage/memory/queue.js +72 -0
- package/dist/storage/memory/threads.d.ts +39 -0
- package/dist/storage/memory/threads.js +115 -0
- package/dist/threads/index.d.ts +36 -0
- package/dist/threads/index.js +26 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +1 -0
- package/dist/utils/getGraph.d.ts +10 -0
- package/dist/utils/getGraph.js +18 -0
- package/dist/utils/getLangGraphCommand.d.ts +9 -0
- package/dist/utils/getLangGraphCommand.js +13 -0
- package/package.json +39 -0
- package/src/adapter/hono/assistants.ts +41 -0
- package/src/adapter/hono/endpoint.ts +4 -0
- package/src/adapter/hono/index.ts +14 -0
- package/src/adapter/hono/runs.ts +102 -0
- package/src/adapter/hono/threads.ts +92 -0
- package/src/adapter/hono/zod.ts +49 -0
- package/src/createEndpoint.ts +106 -0
- package/src/global.ts +6 -0
- package/src/graph/stream.ts +253 -0
- package/src/graph/stringify.ts +219 -0
- package/src/index.ts +5 -0
- package/src/queue/JsonPlusSerializer.ts +143 -0
- package/src/queue/event_message.ts +30 -0
- package/src/queue/stream_queue.ts +236 -0
- package/src/storage/index.ts +14 -0
- package/src/storage/memory/checkpoint.ts +2 -0
- package/src/storage/memory/queue.ts +83 -0
- package/src/storage/memory/threads.ts +154 -0
- package/src/threads/index.ts +51 -0
- package/src/types.ts +116 -0
- package/src/utils/getGraph.ts +44 -0
- package/src/utils/getLangGraphCommand.ts +21 -0
- package/test/graph/index.ts +21 -0
- package/test/hono.ts +10 -0
- package/tsconfig.json +20 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Thread, Assistant, Run, StreamMode, Command, Metadata, AssistantGraph, OnConflictBehavior, ThreadStatus, Checkpoint } from '@langchain/langgraph-sdk';
|
|
2
|
+
import { StreamEvent } from '@langchain/core/tracers/log_stream';
|
|
3
|
+
import { EventMessage } from './queue/event_message';
|
|
4
|
+
import { RunnableConfig } from '@langchain/core/runnables';
|
|
5
|
+
export type AssistantSortBy = 'assistant_id' | 'graph_id' | 'name' | 'created_at' | 'updated_at';
|
|
6
|
+
export type ThreadSortBy = 'thread_id' | 'status' | 'created_at' | 'updated_at';
|
|
7
|
+
export type SortOrder = 'asc' | 'desc';
|
|
8
|
+
export type RunStatus = 'pending' | 'running' | 'error' | 'success' | 'timeout' | 'interrupted';
|
|
9
|
+
export type MultitaskStrategy = 'reject' | 'interrupt' | 'rollback' | 'enqueue';
|
|
10
|
+
export type DisconnectMode = 'cancel' | 'continue';
|
|
11
|
+
export type OnCompletionBehavior = 'complete' | 'continue';
|
|
12
|
+
export type CancelAction = 'interrupt' | 'rollback';
|
|
13
|
+
export type StreamInputData = {
|
|
14
|
+
input?: Record<string, unknown> | null;
|
|
15
|
+
metadata?: Metadata;
|
|
16
|
+
config?: RunnableConfig;
|
|
17
|
+
checkpointId?: string;
|
|
18
|
+
checkpoint?: Omit<Checkpoint, 'thread_id'>;
|
|
19
|
+
checkpoint_during?: boolean;
|
|
20
|
+
interrupt_before?: '*' | string[];
|
|
21
|
+
interrupt_after?: '*' | string[];
|
|
22
|
+
multitask_strategy?: MultitaskStrategy;
|
|
23
|
+
on_completion?: OnCompletionBehavior;
|
|
24
|
+
signal?: AbortController['signal'];
|
|
25
|
+
webhook?: string;
|
|
26
|
+
on_disconnect?: DisconnectMode;
|
|
27
|
+
after_seconds?: number;
|
|
28
|
+
if_not_exists?: 'create' | 'reject';
|
|
29
|
+
command?: Command;
|
|
30
|
+
onRunCreated?: (params: {
|
|
31
|
+
run_id: string;
|
|
32
|
+
thread_id?: string;
|
|
33
|
+
}) => void;
|
|
34
|
+
stream_mode?: StreamMode[];
|
|
35
|
+
stream_subgraphs?: boolean;
|
|
36
|
+
stream_resumable?: boolean;
|
|
37
|
+
feedback_keys?: string[];
|
|
38
|
+
temporary?: boolean;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* 兼容 LangGraph SDK 的接口定义,方便进行无侵入式的扩展
|
|
42
|
+
*/
|
|
43
|
+
export interface ILangGraphClient<TStateType = unknown> {
|
|
44
|
+
assistants: {
|
|
45
|
+
search(query?: {
|
|
46
|
+
graphId?: string;
|
|
47
|
+
metadata?: Metadata;
|
|
48
|
+
limit?: number;
|
|
49
|
+
offset?: number;
|
|
50
|
+
sortBy?: AssistantSortBy;
|
|
51
|
+
sortOrder?: SortOrder;
|
|
52
|
+
}): Promise<Assistant[]>;
|
|
53
|
+
getGraph(assistantId: string, options?: {
|
|
54
|
+
xray?: boolean | number;
|
|
55
|
+
}): Promise<AssistantGraph>;
|
|
56
|
+
};
|
|
57
|
+
threads: {
|
|
58
|
+
create(payload?: {
|
|
59
|
+
metadata?: Metadata;
|
|
60
|
+
thread_id?: string;
|
|
61
|
+
if_exists?: OnConflictBehavior;
|
|
62
|
+
graph_id?: string;
|
|
63
|
+
}): Promise<Thread<TStateType>>;
|
|
64
|
+
search(query?: {
|
|
65
|
+
metadata?: Metadata;
|
|
66
|
+
limit?: number;
|
|
67
|
+
offset?: number;
|
|
68
|
+
status?: ThreadStatus;
|
|
69
|
+
sortBy?: ThreadSortBy;
|
|
70
|
+
sortOrder?: SortOrder;
|
|
71
|
+
}): Promise<Thread<TStateType>[]>;
|
|
72
|
+
get(threadId: string): Promise<Thread<TStateType>>;
|
|
73
|
+
delete(threadId: string): Promise<void>;
|
|
74
|
+
};
|
|
75
|
+
runs: {
|
|
76
|
+
list(threadId: string, options?: {
|
|
77
|
+
limit?: number;
|
|
78
|
+
offset?: number;
|
|
79
|
+
status?: RunStatus;
|
|
80
|
+
}): Promise<Run[]>;
|
|
81
|
+
stream(threadId: string, assistantId: string, payload?: StreamInputData): AsyncGenerator<EventMessage>;
|
|
82
|
+
joinStream(threadId: string, runId: string, options?: {
|
|
83
|
+
signal?: AbortSignal;
|
|
84
|
+
cancelOnDisconnect?: boolean;
|
|
85
|
+
lastEventId?: string;
|
|
86
|
+
streamMode?: StreamMode | StreamMode[];
|
|
87
|
+
} | AbortSignal): AsyncGenerator<{
|
|
88
|
+
id?: string;
|
|
89
|
+
event: StreamEvent;
|
|
90
|
+
data: any;
|
|
91
|
+
}>;
|
|
92
|
+
cancel(threadId: string, runId: string, wait?: boolean, action?: CancelAction): Promise<void>;
|
|
93
|
+
};
|
|
94
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseCheckpointSaver, BaseStore, CompiledGraph, CompiledStateGraph, LangGraphRunnableConfig } from '@langchain/langgraph';
|
|
2
|
+
export type CompiledGraphFactory<T extends string> = (config: {
|
|
3
|
+
configurable?: Record<string, unknown>;
|
|
4
|
+
}) => Promise<CompiledGraph<T>>;
|
|
5
|
+
export declare const GRAPHS: Record<string, CompiledGraph<string> | CompiledGraphFactory<string>>;
|
|
6
|
+
export declare function registerGraph(graphId: string, graph: CompiledGraph<any> | CompiledStateGraph<any, any, any, any, any, any, any> | CompiledGraphFactory<any>): Promise<void>;
|
|
7
|
+
export declare function getGraph(graphId: string, config: LangGraphRunnableConfig | undefined, options?: {
|
|
8
|
+
checkpointer?: BaseCheckpointSaver | null;
|
|
9
|
+
store?: BaseStore;
|
|
10
|
+
}): Promise<CompiledGraph<string, any, any, Record<string, any>, any, any, unknown>>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { globalCheckPointer } from '../global';
|
|
2
|
+
export const GRAPHS = {};
|
|
3
|
+
export async function registerGraph(graphId, graph) {
|
|
4
|
+
GRAPHS[graphId] = graph;
|
|
5
|
+
}
|
|
6
|
+
export async function getGraph(graphId, config, options) {
|
|
7
|
+
if (!GRAPHS[graphId])
|
|
8
|
+
throw new Error(`Graph "${graphId}" not found`);
|
|
9
|
+
const compiled = typeof GRAPHS[graphId] === 'function' ? await GRAPHS[graphId](config ?? { configurable: {} }) : GRAPHS[graphId];
|
|
10
|
+
if (typeof options?.checkpointer !== 'undefined') {
|
|
11
|
+
compiled.checkpointer = options?.checkpointer ?? globalCheckPointer;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
compiled.checkpointer = globalCheckPointer;
|
|
15
|
+
}
|
|
16
|
+
compiled.store = options?.store ?? undefined;
|
|
17
|
+
return compiled;
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@langchain/langgraph';
|
|
2
|
+
import { Command as ClientCommand } from '@langgraph-js/sdk';
|
|
3
|
+
export interface RunSend {
|
|
4
|
+
node: string;
|
|
5
|
+
input?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface RunCommand extends ClientCommand {
|
|
8
|
+
}
|
|
9
|
+
export declare const getLangGraphCommand: (command: RunCommand) => Command<unknown, Record<string, unknown>, string>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command, Send } from '@langchain/langgraph';
|
|
2
|
+
export const getLangGraphCommand = (command) => {
|
|
3
|
+
let goto = command.goto != null && !Array.isArray(command.goto) ? [command.goto] : command.goto;
|
|
4
|
+
return new Command({
|
|
5
|
+
goto: goto?.map((item) => {
|
|
6
|
+
if (typeof item !== 'string')
|
|
7
|
+
return new Send(item.node, item.input);
|
|
8
|
+
return item;
|
|
9
|
+
}),
|
|
10
|
+
update: command.update ?? undefined,
|
|
11
|
+
resume: command.resume,
|
|
12
|
+
});
|
|
13
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@langgraph-js/pure-graph",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"/hono": {
|
|
11
|
+
"import": "./dist/adapter/hono/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"author": "",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@langchain/core": "^0.3.77",
|
|
19
|
+
"@langchain/langgraph": "^0.4.9",
|
|
20
|
+
"@langchain/langgraph-checkpoint": "^0.1.1",
|
|
21
|
+
"@langgraph-js/pro": "^1.8.1",
|
|
22
|
+
"eventemitter3": "^5.0.1",
|
|
23
|
+
"zod": "^3"
|
|
24
|
+
},
|
|
25
|
+
"optionalDependencies": {
|
|
26
|
+
"@hono/zod-validator": "^0.7.3",
|
|
27
|
+
"hono": "^4.9.9"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@langchain/langgraph-sdk": "^0.1.7",
|
|
31
|
+
"@langgraph-js/sdk": "^2.0.1",
|
|
32
|
+
"@types/node": "^22.13.5",
|
|
33
|
+
"typescript": "^5.9.2"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "bun run test/hono.ts",
|
|
37
|
+
"build": "tsc"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { zValidator } from '@hono/zod-validator';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { client } from './endpoint';
|
|
5
|
+
import { MetadataSchema } from './zod';
|
|
6
|
+
const api = new Hono();
|
|
7
|
+
|
|
8
|
+
api.post(
|
|
9
|
+
'/assistants/search',
|
|
10
|
+
zValidator(
|
|
11
|
+
'json',
|
|
12
|
+
z.object({
|
|
13
|
+
graph_id: z.string().optional(),
|
|
14
|
+
metadata: MetadataSchema.optional(),
|
|
15
|
+
limit: z.number().int().optional(),
|
|
16
|
+
offset: z.number().int().optional(),
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
async (c) => {
|
|
20
|
+
// Search Assistants
|
|
21
|
+
const payload = c.req.valid('json');
|
|
22
|
+
let total = 0;
|
|
23
|
+
const data = await client.assistants.search(payload);
|
|
24
|
+
c.res.headers.set('X-Pagination-Total', total.toString());
|
|
25
|
+
return c.json(data);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
api.get(
|
|
30
|
+
'/assistants/:assistant_id/graph',
|
|
31
|
+
zValidator('query', z.object({ xray: z.string().optional() })),
|
|
32
|
+
async (c) => {
|
|
33
|
+
const xray = c.req.valid('query').xray;
|
|
34
|
+
const data = await client.assistants.getGraph(c.req.param('assistant_id'), {
|
|
35
|
+
xray: xray !== undefined ? xray === 'true' : undefined,
|
|
36
|
+
});
|
|
37
|
+
return c.json(data);
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export default api;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import Assistants from './assistants';
|
|
3
|
+
import Runs from './runs';
|
|
4
|
+
import Threads from './threads';
|
|
5
|
+
import { cors } from 'hono/cors';
|
|
6
|
+
const app = new Hono();
|
|
7
|
+
|
|
8
|
+
app.use(cors());
|
|
9
|
+
|
|
10
|
+
app.route('/', Assistants);
|
|
11
|
+
app.route('/', Runs);
|
|
12
|
+
app.route('/', Threads);
|
|
13
|
+
|
|
14
|
+
export default app;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { zValidator } from '@hono/zod-validator';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
|
|
4
|
+
import { streamSSE } from 'hono/streaming';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { client } from './endpoint';
|
|
7
|
+
import { AssistantConfig, CommandSchema, MetadataSchema } from './zod';
|
|
8
|
+
import { serialiseAsDict } from '../../graph/stream';
|
|
9
|
+
|
|
10
|
+
const api = new Hono();
|
|
11
|
+
|
|
12
|
+
// 最常用的对话接口
|
|
13
|
+
api.post(
|
|
14
|
+
'/threads/:thread_id/runs/stream',
|
|
15
|
+
zValidator('param', z.object({ thread_id: z.string().uuid() })),
|
|
16
|
+
zValidator(
|
|
17
|
+
'json',
|
|
18
|
+
z
|
|
19
|
+
.object({
|
|
20
|
+
assistant_id: z.union([z.string().uuid(), z.string()]),
|
|
21
|
+
checkpoint_id: z.string().optional(),
|
|
22
|
+
// checkpoint: CheckpointSchema.optional(),
|
|
23
|
+
input: z.any().optional(),
|
|
24
|
+
command: CommandSchema.optional(),
|
|
25
|
+
metadata: MetadataSchema.optional(),
|
|
26
|
+
config: AssistantConfig.optional(),
|
|
27
|
+
webhook: z.string().optional(),
|
|
28
|
+
interrupt_before: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
29
|
+
interrupt_after: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
30
|
+
on_disconnect: z.enum(['cancel', 'continue']).optional().default('continue'),
|
|
31
|
+
multitask_strategy: z.enum(['reject', 'rollback', 'interrupt', 'enqueue']).optional(),
|
|
32
|
+
stream_mode: z
|
|
33
|
+
.array(z.enum(['values', 'messages', 'messages-tuple', 'updates', 'events', 'debug', 'custom']))
|
|
34
|
+
.optional(),
|
|
35
|
+
stream_subgraphs: z.boolean().optional(),
|
|
36
|
+
stream_resumable: z.boolean().optional(),
|
|
37
|
+
after_seconds: z.number().optional(),
|
|
38
|
+
if_not_exists: z.enum(['create', 'reject']).optional(),
|
|
39
|
+
on_completion: z.enum(['complete', 'continue']).optional(),
|
|
40
|
+
feedback_keys: z.array(z.string()).optional(),
|
|
41
|
+
langsmith_tracer: z.unknown().optional(),
|
|
42
|
+
})
|
|
43
|
+
.describe('Payload for creating a stateful run.'),
|
|
44
|
+
),
|
|
45
|
+
async (c) => {
|
|
46
|
+
// Stream Run
|
|
47
|
+
const { thread_id } = c.req.valid('param');
|
|
48
|
+
const payload = c.req.valid('json');
|
|
49
|
+
|
|
50
|
+
// c.header('Content-Location', `/threads/${thread_id}/runs/${run.run_id}`);
|
|
51
|
+
return streamSSE(c, async (stream) => {
|
|
52
|
+
/** @ts-ignore zod v3 的问题,与 ts 类型不一致 */
|
|
53
|
+
for await (const { event, data } of client.runs.stream(thread_id, payload.assistant_id, payload)) {
|
|
54
|
+
await stream.writeSSE({ data: serialiseAsDict(data), event });
|
|
55
|
+
}
|
|
56
|
+
// await stream.sleep(500); // 不知为何要等
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
api.get(
|
|
62
|
+
'/threads/:thread_id/runs',
|
|
63
|
+
zValidator('param', z.object({ thread_id: z.string().uuid() })),
|
|
64
|
+
zValidator(
|
|
65
|
+
'query',
|
|
66
|
+
z.object({
|
|
67
|
+
limit: z.string().optional(),
|
|
68
|
+
offset: z.string().optional(),
|
|
69
|
+
status: z.enum(['pending', 'running', 'error', 'success', 'timeout', 'interrupted']).optional(),
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
async (c) => {
|
|
73
|
+
const { thread_id } = c.req.valid('param');
|
|
74
|
+
const { limit, offset, status } = c.req.valid('query');
|
|
75
|
+
const runs = await client.runs.list(thread_id, { limit: Number(limit), offset: Number(offset), status });
|
|
76
|
+
return c.json(runs);
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
api.post(
|
|
81
|
+
'/threads/:thread_id/runs/:run_id/cancel',
|
|
82
|
+
zValidator('param', z.object({ thread_id: z.string().uuid(), run_id: z.string().uuid() })),
|
|
83
|
+
zValidator(
|
|
84
|
+
'query',
|
|
85
|
+
z.object({
|
|
86
|
+
wait: z.coerce.boolean().optional().default(false),
|
|
87
|
+
action: z.enum(['interrupt', 'rollback']).optional().default('interrupt'),
|
|
88
|
+
}),
|
|
89
|
+
),
|
|
90
|
+
async (c) => {
|
|
91
|
+
// Cancel Run Http
|
|
92
|
+
const { thread_id, run_id } = c.req.valid('param');
|
|
93
|
+
const { wait, action } = c.req.valid('query');
|
|
94
|
+
const cancel = client.runs.cancel(thread_id, run_id, wait, action);
|
|
95
|
+
if (wait) {
|
|
96
|
+
await cancel;
|
|
97
|
+
}
|
|
98
|
+
return c.body(null, wait ? 204 : 202);
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
export default api;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { zValidator } from '@hono/zod-validator';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { client } from './endpoint';
|
|
5
|
+
import { CommandSchema, MetadataSchema } from './zod';
|
|
6
|
+
|
|
7
|
+
const api = new Hono();
|
|
8
|
+
|
|
9
|
+
// Threads Routes
|
|
10
|
+
api.post(
|
|
11
|
+
'/threads',
|
|
12
|
+
zValidator(
|
|
13
|
+
'json',
|
|
14
|
+
z
|
|
15
|
+
.object({
|
|
16
|
+
// supersteps: z
|
|
17
|
+
// .array(
|
|
18
|
+
// z.object({
|
|
19
|
+
// updates: z.array(
|
|
20
|
+
// z.object({
|
|
21
|
+
// values: z.unknown(),
|
|
22
|
+
// command: CommandSchema.optional(),
|
|
23
|
+
// as_node: z.string(),
|
|
24
|
+
// }),
|
|
25
|
+
// ),
|
|
26
|
+
// }),
|
|
27
|
+
// )
|
|
28
|
+
// .describe('The supersteps to apply to the thread.')
|
|
29
|
+
// .optional(),
|
|
30
|
+
thread_id: z
|
|
31
|
+
.string()
|
|
32
|
+
.uuid()
|
|
33
|
+
.describe('The ID of the thread. If not provided, an ID is generated.')
|
|
34
|
+
.optional(),
|
|
35
|
+
metadata: MetadataSchema.optional(),
|
|
36
|
+
if_exists: z.union([z.literal('raise'), z.literal('do_nothing')]).optional(),
|
|
37
|
+
})
|
|
38
|
+
.describe('Payload for creating a thread.'),
|
|
39
|
+
),
|
|
40
|
+
async (c) => {
|
|
41
|
+
const payload = c.req.valid('json');
|
|
42
|
+
const thread = await client.threads.create(payload);
|
|
43
|
+
|
|
44
|
+
return c.json(thread);
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
api.post(
|
|
49
|
+
'/threads/search',
|
|
50
|
+
zValidator(
|
|
51
|
+
'json',
|
|
52
|
+
z
|
|
53
|
+
.object({
|
|
54
|
+
metadata: z.record(z.unknown()).describe('Metadata to search for.').optional(),
|
|
55
|
+
status: z
|
|
56
|
+
.enum(['idle', 'busy', 'interrupted', 'error'])
|
|
57
|
+
.describe('Filter by thread status.')
|
|
58
|
+
.optional(),
|
|
59
|
+
values: z.record(z.unknown()).describe('Filter by thread values.').optional(),
|
|
60
|
+
limit: z.number().int().gte(1).lte(1000).describe('Maximum number to return.').optional(),
|
|
61
|
+
offset: z.number().int().gte(0).describe('Offset to start from.').optional(),
|
|
62
|
+
sort_by: z
|
|
63
|
+
.enum(['thread_id', 'status', 'created_at', 'updated_at'])
|
|
64
|
+
.describe('Sort by field.')
|
|
65
|
+
.optional(),
|
|
66
|
+
sort_order: z.enum(['asc', 'desc']).describe('Sort order.').optional(),
|
|
67
|
+
})
|
|
68
|
+
.describe('Payload for listing threads.'),
|
|
69
|
+
),
|
|
70
|
+
async (c) => {
|
|
71
|
+
// Search Threads
|
|
72
|
+
const payload = c.req.valid('json');
|
|
73
|
+
const result = await client.threads.search(payload as any);
|
|
74
|
+
c.res.headers.set('X-Pagination-Total', result.length.toString());
|
|
75
|
+
return c.json(result);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
api.get('/threads/:thread_id', zValidator('param', z.object({ thread_id: z.string().uuid() })), async (c) => {
|
|
80
|
+
// Get Thread
|
|
81
|
+
const { thread_id } = c.req.valid('param');
|
|
82
|
+
return c.json(await client.threads.get(thread_id));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
api.delete('/threads/:thread_id', zValidator('param', z.object({ thread_id: z.string().uuid() })), async (c) => {
|
|
86
|
+
// Delete Thread
|
|
87
|
+
const { thread_id } = c.req.valid('param');
|
|
88
|
+
await client.threads.delete(thread_id);
|
|
89
|
+
return new Response(null, { status: 204 });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export default api;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
3
|
+
export const AssistantConfigurable = z
|
|
4
|
+
.object({
|
|
5
|
+
thread_id: z.string().optional(),
|
|
6
|
+
thread_ts: z.string().optional(),
|
|
7
|
+
})
|
|
8
|
+
.catchall(z.unknown());
|
|
9
|
+
|
|
10
|
+
export const AssistantConfig = z
|
|
11
|
+
.object({
|
|
12
|
+
tags: z.array(z.string()).optional(),
|
|
13
|
+
recursion_limit: z.number().int().optional(),
|
|
14
|
+
configurable: AssistantConfigurable.optional(),
|
|
15
|
+
})
|
|
16
|
+
.catchall(z.unknown())
|
|
17
|
+
.describe('The configuration of an assistant.');
|
|
18
|
+
|
|
19
|
+
export const Assistant = z.object({
|
|
20
|
+
assistant_id: z.string().uuid(),
|
|
21
|
+
graph_id: z.string(),
|
|
22
|
+
config: AssistantConfig,
|
|
23
|
+
created_at: z.string(),
|
|
24
|
+
updated_at: z.string(),
|
|
25
|
+
metadata: z.object({}).catchall(z.any()),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const MetadataSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
source: z.union([z.literal('input'), z.literal('loop'), z.literal('update'), z.string()]).optional(),
|
|
31
|
+
step: z.number().optional(),
|
|
32
|
+
writes: z.record(z.unknown()).nullable().optional(),
|
|
33
|
+
parents: z.record(z.string()).optional(),
|
|
34
|
+
})
|
|
35
|
+
.catchall(z.unknown());
|
|
36
|
+
|
|
37
|
+
export const SendSchema = z.object({
|
|
38
|
+
node: z.string(),
|
|
39
|
+
input: z.unknown().nullable(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const CommandSchema = z.object({
|
|
43
|
+
update: z
|
|
44
|
+
.union([z.record(z.unknown()), z.array(z.tuple([z.string(), z.unknown()]))])
|
|
45
|
+
.nullable()
|
|
46
|
+
.optional(),
|
|
47
|
+
resume: z.unknown().optional(),
|
|
48
|
+
goto: z.union([SendSchema, z.array(SendSchema), z.string(), z.array(z.string())]).optional(),
|
|
49
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { StreamEvent } from '@langchain/core/tracers/log_stream';
|
|
2
|
+
import { streamState } from './graph/stream.js';
|
|
3
|
+
import { Assistant, Run, StreamMode, Metadata, AssistantGraph } from '@langchain/langgraph-sdk';
|
|
4
|
+
import { getGraph, GRAPHS } from './utils/getGraph.js';
|
|
5
|
+
import { BaseThreadsManager } from './threads/index.js';
|
|
6
|
+
import { globalMessageQueue } from './global.js';
|
|
7
|
+
import { AssistantSortBy, CancelAction, ILangGraphClient, RunStatus, SortOrder, StreamInputData } from './types.js';
|
|
8
|
+
export { registerGraph } from './utils/getGraph.js';
|
|
9
|
+
|
|
10
|
+
export const AssistantEndpoint: ILangGraphClient['assistants'] = {
|
|
11
|
+
async search(query?: {
|
|
12
|
+
graphId?: string;
|
|
13
|
+
metadata?: Metadata;
|
|
14
|
+
limit?: number;
|
|
15
|
+
offset?: number;
|
|
16
|
+
sortBy?: AssistantSortBy;
|
|
17
|
+
sortOrder?: SortOrder;
|
|
18
|
+
}): Promise<Assistant[]> {
|
|
19
|
+
if (query?.graphId) {
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
assistant_id: query.graphId,
|
|
23
|
+
graph_id: query.graphId,
|
|
24
|
+
config: {},
|
|
25
|
+
created_at: new Date().toISOString(),
|
|
26
|
+
updated_at: new Date().toISOString(),
|
|
27
|
+
metadata: {},
|
|
28
|
+
version: 1,
|
|
29
|
+
name: query.graphId,
|
|
30
|
+
description: '',
|
|
31
|
+
} as Assistant,
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
return Object.entries(GRAPHS).map(
|
|
35
|
+
([graphId, _]) =>
|
|
36
|
+
({
|
|
37
|
+
assistant_id: graphId,
|
|
38
|
+
graph_id: graphId,
|
|
39
|
+
config: {},
|
|
40
|
+
metadata: {},
|
|
41
|
+
version: 1,
|
|
42
|
+
name: graphId,
|
|
43
|
+
description: '',
|
|
44
|
+
created_at: new Date().toISOString(),
|
|
45
|
+
updated_at: new Date().toISOString(),
|
|
46
|
+
} as Assistant),
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
async getGraph(assistantId: string, options?: { xray?: boolean | number }): Promise<AssistantGraph> {
|
|
50
|
+
const config = {};
|
|
51
|
+
const graph = await getGraph(assistantId, config);
|
|
52
|
+
const drawable = await graph.getGraphAsync({
|
|
53
|
+
...config,
|
|
54
|
+
xray: options?.xray ?? undefined,
|
|
55
|
+
});
|
|
56
|
+
return drawable.toJSON() as AssistantGraph;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const createEndpoint = (threads: BaseThreadsManager): ILangGraphClient => {
|
|
61
|
+
return {
|
|
62
|
+
assistants: AssistantEndpoint,
|
|
63
|
+
threads,
|
|
64
|
+
runs: {
|
|
65
|
+
list(threadId: string, options?: { limit?: number; offset?: number; status?: RunStatus }): Promise<Run[]> {
|
|
66
|
+
return threads.listRuns(threadId, options);
|
|
67
|
+
},
|
|
68
|
+
async cancel(threadId: string, runId: string, wait?: boolean, action?: CancelAction): Promise<void> {
|
|
69
|
+
return globalMessageQueue.cancelQueue(runId);
|
|
70
|
+
},
|
|
71
|
+
async *stream(threadId: string, assistantId: string, payload: StreamInputData) {
|
|
72
|
+
if (!payload.config) {
|
|
73
|
+
payload.config = {
|
|
74
|
+
configurable: {
|
|
75
|
+
graph_id: assistantId,
|
|
76
|
+
thread_id: threadId,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const run = threads.createRun(threadId, assistantId, payload);
|
|
82
|
+
|
|
83
|
+
for await (const data of streamState(threads, run, payload, {
|
|
84
|
+
attempt: 0,
|
|
85
|
+
getGraph,
|
|
86
|
+
})) {
|
|
87
|
+
yield data;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
joinStream(
|
|
91
|
+
threadId: string,
|
|
92
|
+
runId: string,
|
|
93
|
+
options?:
|
|
94
|
+
| {
|
|
95
|
+
signal?: AbortSignal;
|
|
96
|
+
cancelOnDisconnect?: boolean;
|
|
97
|
+
lastEventId?: string;
|
|
98
|
+
streamMode?: StreamMode | StreamMode[];
|
|
99
|
+
}
|
|
100
|
+
| AbortSignal,
|
|
101
|
+
): AsyncGenerator<{ id?: string; event: StreamEvent; data: any }> {
|
|
102
|
+
throw new Error('Function not implemented.');
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
};
|