create-relkit 0.4.0 → 0.5.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/add-name.d.ts +9 -0
- package/dist/add-name.d.ts.map +1 -0
- package/dist/add-name.js +18 -0
- package/dist/add-name.js.map +1 -0
- package/dist/add-options-parser.d.ts +21 -0
- package/dist/add-options-parser.d.ts.map +1 -0
- package/dist/add-options-parser.js +103 -0
- package/dist/add-options-parser.js.map +1 -0
- package/dist/add-options-resource.d.ts +5 -0
- package/dist/add-options-resource.d.ts.map +1 -0
- package/dist/add-options-resource.js +18 -0
- package/dist/add-options-resource.js.map +1 -0
- package/dist/add-options.d.ts +6 -0
- package/dist/add-options.d.ts.map +1 -0
- package/dist/add-options.js +165 -0
- package/dist/add-options.js.map +1 -0
- package/dist/add-output.d.ts +4 -0
- package/dist/add-output.d.ts.map +1 -0
- package/dist/add-output.js +33 -0
- package/dist/add-output.js.map +1 -0
- package/dist/add-plan.d.ts +4 -0
- package/dist/add-plan.d.ts.map +1 -0
- package/dist/add-plan.js +62 -0
- package/dist/add-plan.js.map +1 -0
- package/dist/add-resolution-discovery.d.ts +13 -0
- package/dist/add-resolution-discovery.d.ts.map +1 -0
- package/dist/add-resolution-discovery.js +70 -0
- package/dist/add-resolution-discovery.js.map +1 -0
- package/dist/add-resolution-state.d.ts +23 -0
- package/dist/add-resolution-state.d.ts.map +1 -0
- package/dist/add-resolution-state.js +89 -0
- package/dist/add-resolution-state.js.map +1 -0
- package/dist/add-resolver-domain.d.ts +4 -0
- package/dist/add-resolver-domain.d.ts.map +1 -0
- package/dist/add-resolver-domain.js +150 -0
- package/dist/add-resolver-domain.js.map +1 -0
- package/dist/add-resolver-platform.d.ts +4 -0
- package/dist/add-resolver-platform.d.ts.map +1 -0
- package/dist/add-resolver-platform.js +112 -0
- package/dist/add-resolver-platform.js.map +1 -0
- package/dist/add-resolver-resources.d.ts +4 -0
- package/dist/add-resolver-resources.d.ts.map +1 -0
- package/dist/add-resolver-resources.js +150 -0
- package/dist/add-resolver-resources.js.map +1 -0
- package/dist/add-resolver.d.ts +14 -0
- package/dist/add-resolver.d.ts.map +1 -0
- package/dist/add-resolver.js +41 -0
- package/dist/add-resolver.js.map +1 -0
- package/dist/add-transaction-files.d.ts +12 -0
- package/dist/add-transaction-files.d.ts.map +1 -0
- package/dist/add-transaction-files.js +103 -0
- package/dist/add-transaction-files.js.map +1 -0
- package/dist/add-transaction.d.ts +17 -0
- package/dist/add-transaction.d.ts.map +1 -0
- package/dist/add-transaction.js +115 -0
- package/dist/add-transaction.js.map +1 -0
- package/dist/add-types.d.ts +136 -0
- package/dist/add-types.d.ts.map +1 -0
- package/dist/add-types.js +41 -0
- package/dist/add-types.js.map +1 -0
- package/dist/bucket-profiles.d.ts +4 -0
- package/dist/bucket-profiles.d.ts.map +1 -0
- package/dist/bucket-profiles.js +18 -0
- package/dist/bucket-profiles.js.map +1 -0
- package/dist/create-additions.d.ts +7 -0
- package/dist/create-additions.d.ts.map +1 -0
- package/dist/create-additions.js +27 -0
- package/dist/create-additions.js.map +1 -0
- package/dist/create-preview.d.ts +12 -0
- package/dist/create-preview.d.ts.map +1 -0
- package/dist/create-preview.js +50 -0
- package/dist/create-preview.js.map +1 -0
- package/dist/create-resolver.d.ts +14 -0
- package/dist/create-resolver.d.ts.map +1 -0
- package/dist/create-resolver.js +96 -0
- package/dist/create-resolver.js.map +1 -0
- package/dist/domain-planning.d.ts +24 -0
- package/dist/domain-planning.d.ts.map +1 -0
- package/dist/domain-planning.js +94 -0
- package/dist/domain-planning.js.map +1 -0
- package/dist/generate-files-utilities.d.ts +18 -0
- package/dist/generate-files-utilities.d.ts.map +1 -0
- package/dist/generate-files-utilities.js +87 -0
- package/dist/generate-files-utilities.js.map +1 -0
- package/dist/generate-files.d.ts +1 -12
- package/dist/generate-files.d.ts.map +1 -1
- package/dist/generate-files.js +38 -89
- package/dist/generate-files.js.map +1 -1
- package/dist/generate-output.d.ts +2 -1
- package/dist/generate-output.d.ts.map +1 -1
- package/dist/generate-output.js +9 -0
- package/dist/generate-output.js.map +1 -1
- package/dist/generate-process.d.ts +1 -1
- package/dist/generate-process.d.ts.map +1 -1
- package/dist/generate-process.js +7 -3
- package/dist/generate-process.js.map +1 -1
- package/dist/generate-types.d.ts +46 -0
- package/dist/generate-types.d.ts.map +1 -0
- package/dist/generate-types.js +20 -0
- package/dist/generate-types.js.map +1 -0
- package/dist/generate.d.ts +1 -38
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +34 -39
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -9
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +5 -1
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +10 -1
- package/dist/options.js.map +1 -1
- package/dist/plan-builder.d.ts +33 -0
- package/dist/plan-builder.d.ts.map +1 -0
- package/dist/plan-builder.js +139 -0
- package/dist/plan-builder.js.map +1 -0
- package/dist/plan-manifest.d.ts +6 -0
- package/dist/plan-manifest.d.ts.map +1 -0
- package/dist/plan-manifest.js +40 -0
- package/dist/plan-manifest.js.map +1 -0
- package/dist/project-discovery-app.d.ts +7 -0
- package/dist/project-discovery-app.d.ts.map +1 -0
- package/dist/project-discovery-app.js +122 -0
- package/dist/project-discovery-app.js.map +1 -0
- package/dist/project-discovery-database.d.ts +11 -0
- package/dist/project-discovery-database.d.ts.map +1 -0
- package/dist/project-discovery-database.js +46 -0
- package/dist/project-discovery-database.js.map +1 -0
- package/dist/project-discovery-types.d.ts +45 -0
- package/dist/project-discovery-types.d.ts.map +1 -0
- package/dist/project-discovery-types.js +2 -0
- package/dist/project-discovery-types.js.map +1 -0
- package/dist/project-discovery.d.ts +4 -0
- package/dist/project-discovery.d.ts.map +1 -0
- package/dist/project-discovery.js +152 -0
- package/dist/project-discovery.js.map +1 -0
- package/dist/prompt-driver.d.ts +32 -0
- package/dist/prompt-driver.d.ts.map +1 -0
- package/dist/prompt-driver.js +42 -0
- package/dist/prompt-driver.js.map +1 -0
- package/dist/provider-planning-definitions.d.ts +35 -0
- package/dist/provider-planning-definitions.d.ts.map +1 -0
- package/dist/provider-planning-definitions.js +109 -0
- package/dist/provider-planning-definitions.js.map +1 -0
- package/dist/provider-planning.d.ts +11 -0
- package/dist/provider-planning.d.ts.map +1 -0
- package/dist/provider-planning.js +131 -0
- package/dist/provider-planning.js.map +1 -0
- package/dist/render-ai-sources.d.ts +18 -0
- package/dist/render-ai-sources.d.ts.map +1 -0
- package/dist/render-ai-sources.js +49 -0
- package/dist/render-ai-sources.js.map +1 -0
- package/dist/render-ai.d.ts +11 -0
- package/dist/render-ai.d.ts.map +1 -0
- package/dist/render-ai.js +61 -0
- package/dist/render-ai.js.map +1 -0
- package/dist/render-auth-schema.d.ts +3 -0
- package/dist/render-auth-schema.d.ts.map +1 -0
- package/dist/render-auth-schema.js +85 -0
- package/dist/render-auth-schema.js.map +1 -0
- package/dist/render-auth-sources.d.ts +3 -0
- package/dist/render-auth-sources.d.ts.map +1 -0
- package/dist/render-auth-sources.js +17 -0
- package/dist/render-auth-sources.js.map +1 -0
- package/dist/render-auth.d.ts +6 -0
- package/dist/render-auth.d.ts.map +1 -0
- package/dist/render-auth.js +84 -0
- package/dist/render-auth.js.map +1 -0
- package/dist/render-database-sources.d.ts +5 -0
- package/dist/render-database-sources.d.ts.map +1 -0
- package/dist/render-database-sources.js +65 -0
- package/dist/render-database-sources.js.map +1 -0
- package/dist/render-database.d.ts +13 -0
- package/dist/render-database.d.ts.map +1 -0
- package/dist/render-database.js +40 -0
- package/dist/render-database.js.map +1 -0
- package/dist/render-domain-register.d.ts +4 -0
- package/dist/render-domain-register.d.ts.map +1 -0
- package/dist/render-domain-register.js +11 -0
- package/dist/render-domain-register.js.map +1 -0
- package/dist/render-domain-sources.d.ts +19 -0
- package/dist/render-domain-sources.d.ts.map +1 -0
- package/dist/render-domain-sources.js +132 -0
- package/dist/render-domain-sources.js.map +1 -0
- package/dist/render-domain.d.ts +26 -0
- package/dist/render-domain.d.ts.map +1 -0
- package/dist/render-domain.js +98 -0
- package/dist/render-domain.js.map +1 -0
- package/dist/render-resource-sources.d.ts +4 -0
- package/dist/render-resource-sources.d.ts.map +1 -0
- package/dist/render-resource-sources.js +31 -0
- package/dist/render-resource-sources.js.map +1 -0
- package/dist/render-resources.d.ts +11 -0
- package/dist/render-resources.d.ts.map +1 -0
- package/dist/render-resources.js +53 -0
- package/dist/render-resources.js.map +1 -0
- package/dist/render-route-sources.d.ts +6 -0
- package/dist/render-route-sources.d.ts.map +1 -0
- package/dist/render-route-sources.js +35 -0
- package/dist/render-route-sources.js.map +1 -0
- package/dist/render-routes.d.ts +13 -0
- package/dist/render-routes.d.ts.map +1 -0
- package/dist/render-routes.js +48 -0
- package/dist/render-routes.js.map +1 -0
- package/dist/render-service.d.ts +6 -0
- package/dist/render-service.d.ts.map +1 -0
- package/dist/render-service.js +124 -0
- package/dist/render-service.js.map +1 -0
- package/dist/scaffold-catalog.d.ts +75 -0
- package/dist/scaffold-catalog.d.ts.map +1 -0
- package/dist/scaffold-catalog.js +24 -0
- package/dist/scaffold-catalog.js.map +1 -0
- package/dist/source-edit.d.ts +10 -0
- package/dist/source-edit.d.ts.map +1 -0
- package/dist/source-edit.js +136 -0
- package/dist/source-edit.js.map +1 -0
- package/dist/source-import-edit.d.ts +3 -0
- package/dist/source-import-edit.d.ts.map +1 -0
- package/dist/source-import-edit.js +52 -0
- package/dist/source-import-edit.js.map +1 -0
- package/dist/template-root.d.ts +5 -0
- package/dist/template-root.d.ts.map +1 -0
- package/dist/template-root.js +15 -0
- package/dist/template-root.js.map +1 -0
- package/dist/templates/default/README.md +1 -1
- package/dist/templates/default/v1/agent/.env.example +0 -2
- package/dist/templates/default/v1/agent/README.md +7 -8
- package/dist/templates/default/v1/agent/package.json +6 -4
- package/dist/templates/default/v1/agent/relkit.config.ts +5 -15
- package/dist/templates/default/v1/agent/src/hello/agents/assistant.agent.ts +37 -4
- package/dist/templates/default/v1/agent/src/hello/agents/review.agent.ts +43 -0
- package/dist/templates/default/v1/agent/src/platform/env.ts +0 -2
- package/dist/templates/default/v1/agent/tests/integration/hello.route.test.ts +17 -6
- package/dist/templates/default/v1/agent/tests/unit/assistant.agent.test.ts +14 -36
- package/dist/templates/default/v1/agent/tests/unit/review.agent.test.ts +19 -0
- package/dist/templates/default/v1/api/package.json +3 -3
- package/dist/templates/default/v1/fullstack/.env.example +1 -0
- package/dist/templates/default/v1/fullstack/README.md +15 -0
- package/dist/templates/default/v1/fullstack/gitignore +4 -0
- package/dist/templates/default/v1/fullstack/package.json +35 -0
- package/dist/templates/default/v1/fullstack/relkit.config.ts +15 -0
- package/dist/templates/default/v1/fullstack/src/hello/agents/assistant.agent.ts +46 -0
- package/dist/templates/default/v1/fullstack/src/hello/agents/review.agent.ts +43 -0
- package/dist/templates/default/v1/fullstack/src/hello/functions/hello.function.ts +10 -0
- package/dist/templates/default/v1/fullstack/src/hello/service.ts +4 -0
- package/dist/templates/default/v1/fullstack/src/hello/tools/lookup.tool.ts +9 -0
- package/dist/templates/default/v1/fullstack/src/platform/env.ts +5 -0
- package/dist/templates/default/v1/fullstack/src/routes/hello/route.ts +4 -0
- package/dist/templates/default/v1/fullstack/tests/integration/hello.route.test.ts +29 -0
- package/dist/templates/default/v1/fullstack/tests/unit/assistant.agent.test.ts +18 -0
- package/dist/templates/default/v1/fullstack/tests/unit/hello.function.test.ts +7 -0
- package/dist/templates/default/v1/fullstack/tests/unit/review.agent.test.ts +19 -0
- package/dist/templates/default/v1/fullstack/tsconfig.json +21 -0
- package/dist/templates/default/v1/fullstack/web/app/agent-config.ts +7 -0
- package/dist/templates/default/v1/fullstack/web/app/layout.tsx +12 -0
- package/dist/templates/default/v1/fullstack/web/app/page.tsx +85 -0
- package/dist/templates/default/v1/fullstack/web/app/providers.tsx +29 -0
- package/dist/templates/default/v1/fullstack/web/next-env.d.ts +2 -0
- package/dist/templates/default/v1/fullstack/web/next.config.ts +5 -0
- package/dist/templates/default/v1/fullstack/web/tsconfig.json +10 -0
- package/dist/templates/default/v1/minimal/package.json +3 -3
- package/dist/templates/default/v1/tasks/.env.example +2 -0
- package/dist/templates/default/v1/tasks/README.md +15 -0
- package/dist/templates/default/v1/tasks/examples/export-orders.json +1 -0
- package/dist/templates/default/v1/tasks/gitignore +10 -0
- package/dist/templates/default/v1/tasks/package.json +24 -0
- package/dist/templates/default/v1/tasks/relkit.config.ts +11 -0
- package/dist/templates/default/v1/tasks/src/orders/functions/health.function.ts +9 -0
- package/dist/templates/default/v1/tasks/src/orders/jobs/export-orders.job.ts +15 -0
- package/dist/templates/default/v1/tasks/src/orders/service.ts +10 -0
- package/dist/templates/default/v1/tasks/src/orders/tasks/export-orders.task.ts +19 -0
- package/dist/templates/default/v1/tasks/src/platform/env.ts +3 -0
- package/dist/templates/default/v1/tasks/src/routes/health/route.ts +4 -0
- package/dist/templates/default/v1/tasks/tests/unit/export-orders.task.test.ts +7 -0
- package/dist/templates/default/v1/tasks/tsconfig.json +14 -0
- package/package.json +6 -1
- package/dist/templates/default/v1/agent/src/hello/functions/ask-assistant.function.ts +0 -16
|
@@ -1,16 +1,49 @@
|
|
|
1
1
|
import { defineAgent } from "@relkit/app/agents";
|
|
2
2
|
import { z } from "@relkit/app/schema";
|
|
3
|
+
import { FakeToolCallingModel, todoListMiddleware, tool } from "langchain";
|
|
3
4
|
import lookup from "@app/hello/tools/lookup.tool.js";
|
|
4
5
|
|
|
6
|
+
export const uppercase = tool(async ({ text }) => text.toUpperCase(), {
|
|
7
|
+
name: "uppercase",
|
|
8
|
+
description: "Uppercase text without leaving the process",
|
|
9
|
+
schema: z.object({ text: z.string() }),
|
|
10
|
+
});
|
|
11
|
+
|
|
5
12
|
export default defineAgent({
|
|
6
13
|
id: "hello.assistant",
|
|
7
14
|
|
|
8
|
-
input: z.object({
|
|
15
|
+
input: z.object({ message: z.string().min(1) }),
|
|
9
16
|
output: z.object({ answer: z.string() }),
|
|
10
17
|
|
|
11
|
-
model:
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
model: new FakeToolCallingModel({
|
|
19
|
+
toolCalls: [
|
|
20
|
+
[
|
|
21
|
+
{
|
|
22
|
+
name: "write_todos",
|
|
23
|
+
args: { todos: [{ content: "Answer the greeting", status: "completed" }] },
|
|
24
|
+
id: "todo-1",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
{
|
|
29
|
+
name: "relkit_output",
|
|
30
|
+
args: { value: { answer: "Hello from the offline LangChain model." } },
|
|
31
|
+
id: "output-1",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
instructions: "Answer greeting questions with the available tools.",
|
|
37
|
+
tools: [lookup, uppercase],
|
|
38
|
+
middleware: [todoListMiddleware()],
|
|
39
|
+
stateProfile: "default",
|
|
40
|
+
client: {
|
|
41
|
+
public: true,
|
|
42
|
+
state: ["todos"],
|
|
43
|
+
events: { notice: z.object({ message: z.string() }) },
|
|
44
|
+
},
|
|
45
|
+
chat: { input: "message", output: "answer" },
|
|
46
|
+
controls: ["stop"],
|
|
14
47
|
|
|
15
48
|
limits: {
|
|
16
49
|
maxSteps: 4,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineGraph, defineGraphNode } from "@relkit/app/agents";
|
|
2
|
+
import { z } from "@relkit/app/schema";
|
|
3
|
+
import { END, MemorySaver, START, StateSchema, interrupt } from "@langchain/langgraph";
|
|
4
|
+
|
|
5
|
+
const state = new StateSchema({
|
|
6
|
+
request: z.string(),
|
|
7
|
+
approved: z.boolean().optional(),
|
|
8
|
+
result: z.string().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const review = defineGraphNode({
|
|
12
|
+
id: "review",
|
|
13
|
+
input: z.object({ request: z.string() }),
|
|
14
|
+
output: z.object({ approved: z.boolean() }),
|
|
15
|
+
resume: z.boolean(),
|
|
16
|
+
handler: ({ request }) => ({
|
|
17
|
+
approved: interrupt({ question: `Approve "${request}"?` }) as boolean,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const finish = defineGraphNode({
|
|
22
|
+
id: "finish",
|
|
23
|
+
input: z.object({ request: z.string(), approved: z.boolean() }),
|
|
24
|
+
output: z.object({ result: z.string() }),
|
|
25
|
+
handler: ({ request, approved }) => ({
|
|
26
|
+
result: approved ? `${request} approved` : `${request} rejected`,
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export default defineGraph({
|
|
31
|
+
id: "hello.review",
|
|
32
|
+
state,
|
|
33
|
+
input: z.object({ request: z.string().min(1) }),
|
|
34
|
+
output: z.object({ result: z.string() }),
|
|
35
|
+
nodes: [review, finish],
|
|
36
|
+
edges: (graph) =>
|
|
37
|
+
graph.addEdge(START, "review").addEdge("review", "finish").addEdge("finish", END),
|
|
38
|
+
checkpointer: new MemorySaver(),
|
|
39
|
+
stateProfile: "default",
|
|
40
|
+
client: { public: true, state: ["approved", "result"] },
|
|
41
|
+
controls: ["stop"],
|
|
42
|
+
limits: { maxSteps: 4, maxToolCalls: 1, timeoutMs: 10_000 },
|
|
43
|
+
});
|
|
@@ -2,6 +2,4 @@ import { defineEnv, env } from "@relkit/app/config";
|
|
|
2
2
|
|
|
3
3
|
export default defineEnv({
|
|
4
4
|
LOG_LEVEL: env.literal("trace", "debug", "info", "warn", "error").default("info"),
|
|
5
|
-
OPENAI_API_KEY: env.secret().requiredIn("production"),
|
|
6
|
-
ANTHROPIC_API_KEY: env.secret().requiredIn("production"),
|
|
7
5
|
});
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { afterAll, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
createTestAgentStateProvider,
|
|
7
|
+
createTestApplication,
|
|
8
|
+
createTestRealtimeProvider,
|
|
9
|
+
} from "@relkit/testing";
|
|
3
10
|
import config from "../../relkit.config.js";
|
|
4
11
|
|
|
5
|
-
const
|
|
6
|
-
resolveModel: (selector = "test:model") => ({ id: selector, model: {} }),
|
|
7
|
-
};
|
|
12
|
+
const providerRoot = await mkdtemp(join(tmpdir(), "relkit-agent-template-"));
|
|
8
13
|
const testApp = await createTestApplication(config, {
|
|
9
|
-
providers: {
|
|
14
|
+
providers: {
|
|
15
|
+
"agent-state": { default: createTestAgentStateProvider(join(providerRoot, "agents")) },
|
|
16
|
+
realtime: { default: createTestRealtimeProvider(join(providerRoot, "realtime")) },
|
|
17
|
+
},
|
|
10
18
|
});
|
|
11
19
|
|
|
12
20
|
test("GET /hello", async () => {
|
|
@@ -15,4 +23,7 @@ test("GET /hello", async () => {
|
|
|
15
23
|
expect(await response.json()).toEqual({ message: "Hello, Mustafa!" });
|
|
16
24
|
});
|
|
17
25
|
|
|
18
|
-
afterAll(() =>
|
|
26
|
+
afterAll(async () => {
|
|
27
|
+
await testApp.close();
|
|
28
|
+
await rm(providerRoot, { force: true, recursive: true });
|
|
29
|
+
});
|
|
@@ -1,43 +1,21 @@
|
|
|
1
1
|
import { expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
import assistant from "@app/hello/agents/assistant.agent.js";
|
|
4
|
-
import askAssistant from "@app/hello/functions/ask-assistant.function.js";
|
|
5
|
-
import hello from "@app/hello/functions/hello.function.js";
|
|
2
|
+
import { invokeAgent } from "@relkit/app/agents";
|
|
3
|
+
import assistant, { uppercase } from "@app/hello/agents/assistant.agent.js";
|
|
6
4
|
import lookup from "@app/hello/tools/lookup.tool.js";
|
|
7
5
|
|
|
8
|
-
test("assistant
|
|
9
|
-
expect(assistant.
|
|
10
|
-
expect(assistant.
|
|
6
|
+
test("assistant runs native tools and todo middleware without a paid provider", async () => {
|
|
7
|
+
expect(assistant.tools).toEqual([expect.objectContaining({ ref: lookup.ref }), uppercase]);
|
|
8
|
+
expect(assistant.middleware.map((middleware) => middleware.name)).toEqual(["todoListMiddleware"]);
|
|
9
|
+
expect(assistant.client?.state).toEqual(["todos"]);
|
|
11
10
|
expect(assistant.limits).toEqual({ maxSteps: 4, maxToolCalls: 4, timeoutMs: 10_000 });
|
|
12
11
|
|
|
13
|
-
const agent = createTestAgent({
|
|
14
|
-
agent: assistant,
|
|
15
|
-
tools: [lookup],
|
|
16
|
-
engine: {
|
|
17
|
-
invoke: (request) =>
|
|
18
|
-
invokeFunction(hello, request.input as Parameters<typeof hello.invoke>[0]),
|
|
19
|
-
},
|
|
20
|
-
model: { provider: "openai", modelId: "gpt-5-mini" },
|
|
21
|
-
// Script the model's choices while running the real greeting function.
|
|
22
|
-
script: [
|
|
23
|
-
{
|
|
24
|
-
type: "tool-call",
|
|
25
|
-
callId: "call-1",
|
|
26
|
-
toolId: "hello.lookup",
|
|
27
|
-
input: { name: "Ada" },
|
|
28
|
-
},
|
|
29
|
-
{ type: "final", output: { answer: "Hello, Ada!" } },
|
|
30
|
-
],
|
|
31
|
-
});
|
|
32
|
-
|
|
33
12
|
await expect(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
),
|
|
41
|
-
).resolves.toEqual({ answer: "Hello
|
|
42
|
-
expect(agent.model.calls).toHaveLength(2);
|
|
13
|
+
invokeAgent({
|
|
14
|
+
agent: assistant,
|
|
15
|
+
input: { message: "Greet Ada" },
|
|
16
|
+
threadId: "test:assistant",
|
|
17
|
+
tools: [lookup],
|
|
18
|
+
engine: { invoke: async () => ({ message: "Hello, Ada!" }) },
|
|
19
|
+
}),
|
|
20
|
+
).resolves.toEqual({ answer: "Hello from the offline LangChain model." });
|
|
43
21
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { GraphInterruptedError, invokeAgent } from "@relkit/app/agents";
|
|
3
|
+
import review from "@app/hello/agents/review.agent.js";
|
|
4
|
+
|
|
5
|
+
const runtime = {
|
|
6
|
+
agent: review,
|
|
7
|
+
tools: {},
|
|
8
|
+
engine: { invoke: async () => Promise.reject(new Error("Unexpected function invocation")) },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
test("review graph interrupts and resumes on an explicit thread", async () => {
|
|
12
|
+
const threadId = "test:review";
|
|
13
|
+
await expect(
|
|
14
|
+
invokeAgent({ ...runtime, input: { request: "Publish" }, threadId }),
|
|
15
|
+
).rejects.toBeInstanceOf(GraphInterruptedError);
|
|
16
|
+
await expect(invokeAgent({ ...runtime, input: true, threadId, resume: true })).resolves.toEqual({
|
|
17
|
+
result: "Publish approved",
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"graph": "relkit graph print"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@relkit/app": "0.
|
|
18
|
+
"@relkit/app": "0.5.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/bun": "1.3.10",
|
|
22
|
-
"@relkit/cli": "0.
|
|
23
|
-
"@relkit/testing": "0.
|
|
22
|
+
"@relkit/cli": "0.5.0",
|
|
23
|
+
"@relkit/testing": "0.5.0",
|
|
24
24
|
"typescript": "5.9.3"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
APP_ENV=development
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# my-app
|
|
2
|
+
|
|
3
|
+
A Relkit backend and Next.js typed client. The example includes an offline
|
|
4
|
+
native LangChain agent with typed todo/tool state, a LangGraph interrupt and
|
|
5
|
+
resume flow, explicit application-owned thread IDs, and selectable SSE or
|
|
6
|
+
WebSocket streaming. `bun run dev` serves the API on port 3000, the web app on
|
|
7
|
+
port 3001, and the Inspector on port 3210.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bun install
|
|
11
|
+
bun run dev
|
|
12
|
+
bun run test
|
|
13
|
+
bun run typecheck
|
|
14
|
+
bun run build
|
|
15
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"packageManager": "bun@1.3.10",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "bun run check && bun run --parallel dev:api dev:web",
|
|
8
|
+
"dev:api": "RELKIT_ALLOWED_ORIGINS=http://127.0.0.1:3001 relkit dev",
|
|
9
|
+
"dev:web": "next dev web --hostname 127.0.0.1 --port 3001",
|
|
10
|
+
"check": "relkit check",
|
|
11
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p web/tsconfig.json",
|
|
12
|
+
"test": "bun test",
|
|
13
|
+
"build": "relkit build && next build web",
|
|
14
|
+
"start": "relkit start"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@langchain/langgraph": "1.4.14",
|
|
18
|
+
"@relkit/app": "0.5.0",
|
|
19
|
+
"@relkit/client": "0.5.0",
|
|
20
|
+
"@relkit/local": "0.5.0",
|
|
21
|
+
"@tanstack/react-query": "5.102.3",
|
|
22
|
+
"langchain": "1.5.10",
|
|
23
|
+
"next": "16.3.3",
|
|
24
|
+
"react": "19.2.8",
|
|
25
|
+
"react-dom": "19.2.8"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@relkit/cli": "0.5.0",
|
|
29
|
+
"@relkit/testing": "0.5.0",
|
|
30
|
+
"@types/bun": "1.3.10",
|
|
31
|
+
"@types/react": "19.2.18",
|
|
32
|
+
"@types/react-dom": "19.2.7",
|
|
33
|
+
"typescript": "5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineApp } from "@relkit/app/config";
|
|
2
|
+
import { localAgentState, localRealtime } from "@relkit/local";
|
|
3
|
+
import env from "@app/platform/env.js";
|
|
4
|
+
// relkit:create:deployment-imports
|
|
5
|
+
|
|
6
|
+
export default defineApp({
|
|
7
|
+
env,
|
|
8
|
+
realtime: localRealtime(),
|
|
9
|
+
"agent-state": localAgentState(),
|
|
10
|
+
defaults: { realtime: "default", "agent-state": "default" },
|
|
11
|
+
telemetry: { redaction: { mode: "development-redacted", maxBytes: 65_536 } },
|
|
12
|
+
server: { port: 3000, maxBodyBytes: 1_048_576 },
|
|
13
|
+
inspector: { port: 3210 },
|
|
14
|
+
// relkit:create:deployment
|
|
15
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineAgent } from "@relkit/app/agents";
|
|
2
|
+
import { z } from "@relkit/app/schema";
|
|
3
|
+
import { FakeToolCallingModel, todoListMiddleware, tool } from "langchain";
|
|
4
|
+
import lookup from "@app/hello/tools/lookup.tool.js";
|
|
5
|
+
|
|
6
|
+
const uppercase = tool(async ({ text }) => text.toUpperCase(), {
|
|
7
|
+
name: "uppercase",
|
|
8
|
+
description: "Uppercase text without leaving the process",
|
|
9
|
+
schema: z.object({ text: z.string() }),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default defineAgent({
|
|
13
|
+
id: "hello.assistant",
|
|
14
|
+
input: z.object({ message: z.string().min(1) }),
|
|
15
|
+
output: z.object({ answer: z.string() }),
|
|
16
|
+
model: new FakeToolCallingModel({
|
|
17
|
+
toolCalls: [
|
|
18
|
+
[
|
|
19
|
+
{
|
|
20
|
+
name: "write_todos",
|
|
21
|
+
args: { todos: [{ content: "Answer the greeting", status: "completed" }] },
|
|
22
|
+
id: "todo-1",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
{
|
|
27
|
+
name: "relkit_output",
|
|
28
|
+
args: { value: { answer: "Hello from the offline LangChain model." } },
|
|
29
|
+
id: "output-1",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
],
|
|
33
|
+
}),
|
|
34
|
+
instructions: "Answer greeting questions with the available tools.",
|
|
35
|
+
tools: [lookup, uppercase],
|
|
36
|
+
middleware: [todoListMiddleware()],
|
|
37
|
+
stateProfile: "default",
|
|
38
|
+
client: {
|
|
39
|
+
public: true,
|
|
40
|
+
state: ["todos"],
|
|
41
|
+
events: { notice: z.object({ message: z.string() }) },
|
|
42
|
+
},
|
|
43
|
+
chat: { input: "message", output: "answer" },
|
|
44
|
+
controls: ["stop"],
|
|
45
|
+
limits: { maxSteps: 4, maxToolCalls: 4, timeoutMs: 10_000 },
|
|
46
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineGraph, defineGraphNode } from "@relkit/app/agents";
|
|
2
|
+
import { z } from "@relkit/app/schema";
|
|
3
|
+
import { END, MemorySaver, START, StateSchema, interrupt } from "@langchain/langgraph";
|
|
4
|
+
|
|
5
|
+
const state = new StateSchema({
|
|
6
|
+
request: z.string(),
|
|
7
|
+
approved: z.boolean().optional(),
|
|
8
|
+
result: z.string().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const review = defineGraphNode({
|
|
12
|
+
id: "review",
|
|
13
|
+
input: z.object({ request: z.string() }),
|
|
14
|
+
output: z.object({ approved: z.boolean() }),
|
|
15
|
+
resume: z.boolean(),
|
|
16
|
+
handler: ({ request }) => ({
|
|
17
|
+
approved: interrupt({ question: `Approve "${request}"?` }) as boolean,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const finish = defineGraphNode({
|
|
22
|
+
id: "finish",
|
|
23
|
+
input: z.object({ request: z.string(), approved: z.boolean() }),
|
|
24
|
+
output: z.object({ result: z.string() }),
|
|
25
|
+
handler: ({ request, approved }) => ({
|
|
26
|
+
result: approved ? `${request} approved` : `${request} rejected`,
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export default defineGraph({
|
|
31
|
+
id: "hello.review",
|
|
32
|
+
state,
|
|
33
|
+
input: z.object({ request: z.string().min(1) }),
|
|
34
|
+
output: z.object({ result: z.string() }),
|
|
35
|
+
nodes: [review, finish],
|
|
36
|
+
edges: (graph) =>
|
|
37
|
+
graph.addEdge(START, "review").addEdge("review", "finish").addEdge("finish", END),
|
|
38
|
+
checkpointer: new MemorySaver(),
|
|
39
|
+
stateProfile: "default",
|
|
40
|
+
client: { public: true, state: ["approved", "result"] },
|
|
41
|
+
controls: ["stop"],
|
|
42
|
+
limits: { maxSteps: 4, maxToolCalls: 1, timeoutMs: 10_000 },
|
|
43
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineFunction } from "@relkit/app/functions";
|
|
2
|
+
import { z } from "@relkit/app/schema";
|
|
3
|
+
|
|
4
|
+
const hello = defineFunction({
|
|
5
|
+
input: z.object({ name: z.string().min(1).default("world") }),
|
|
6
|
+
output: z.object({ message: z.string() }),
|
|
7
|
+
handler: async ({ name }) => ({ message: `Hello, ${name}!` }),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export default hello;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { afterAll, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
createTestAgentStateProvider,
|
|
7
|
+
createTestApplication,
|
|
8
|
+
createTestRealtimeProvider,
|
|
9
|
+
} from "@relkit/testing";
|
|
10
|
+
import config from "../../relkit.config.js";
|
|
11
|
+
|
|
12
|
+
const providerRoot = await mkdtemp(join(tmpdir(), "relkit-fullstack-template-"));
|
|
13
|
+
const testApp = await createTestApplication(config, {
|
|
14
|
+
providers: {
|
|
15
|
+
"agent-state": { default: createTestAgentStateProvider(join(providerRoot, "agents")) },
|
|
16
|
+
realtime: { default: createTestRealtimeProvider(join(providerRoot, "realtime")) },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("GET /hello", async () => {
|
|
21
|
+
const response = await testApp.http.request("/hello?name=Mustafa");
|
|
22
|
+
expect(response.status).toBe(200);
|
|
23
|
+
expect(await response.json()).toEqual({ message: "Hello, Mustafa!" });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterAll(async () => {
|
|
27
|
+
await testApp.close();
|
|
28
|
+
await rm(providerRoot, { force: true, recursive: true });
|
|
29
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { invokeAgent } from "@relkit/app/agents";
|
|
3
|
+
import assistant from "@app/hello/agents/assistant.agent.js";
|
|
4
|
+
import lookup from "@app/hello/tools/lookup.tool.js";
|
|
5
|
+
|
|
6
|
+
test("assistant runs with native todo middleware and the offline model", async () => {
|
|
7
|
+
expect(assistant.client?.state).toEqual(["todos"]);
|
|
8
|
+
expect(assistant.middleware.map((middleware) => middleware.name)).toEqual(["todoListMiddleware"]);
|
|
9
|
+
await expect(
|
|
10
|
+
invokeAgent({
|
|
11
|
+
agent: assistant,
|
|
12
|
+
input: { message: "Greet Ada" },
|
|
13
|
+
threadId: "test:assistant",
|
|
14
|
+
tools: [lookup],
|
|
15
|
+
engine: { invoke: async () => ({ message: "Hello, Ada!" }) },
|
|
16
|
+
}),
|
|
17
|
+
).resolves.toEqual({ answer: "Hello from the offline LangChain model." });
|
|
18
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { invokeFunction } from "@relkit/testing";
|
|
3
|
+
import hello from "@app/hello/functions/hello.function.js";
|
|
4
|
+
|
|
5
|
+
test("greets by name", async () => {
|
|
6
|
+
await expect(invokeFunction(hello, { name: "Ada" })).resolves.toEqual({ message: "Hello, Ada!" });
|
|
7
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { GraphInterruptedError, invokeAgent } from "@relkit/app/agents";
|
|
3
|
+
import review from "@app/hello/agents/review.agent.js";
|
|
4
|
+
|
|
5
|
+
const runtime = {
|
|
6
|
+
agent: review,
|
|
7
|
+
tools: {},
|
|
8
|
+
engine: { invoke: async () => Promise.reject(new Error("Unexpected function invocation")) },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
test("review graph interrupts and resumes on an explicit thread", async () => {
|
|
12
|
+
const threadId = "test:review";
|
|
13
|
+
await expect(
|
|
14
|
+
invokeAgent({ ...runtime, input: { request: "Publish" }, threadId }),
|
|
15
|
+
).rejects.toBeInstanceOf(GraphInterruptedError);
|
|
16
|
+
await expect(invokeAgent({ ...runtime, input: true, threadId, resume: true })).resolves.toEqual({
|
|
17
|
+
result: "Publish approved",
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "Bundler",
|
|
7
|
+
"baseUrl": ".",
|
|
8
|
+
"paths": { "@app/*": ["src/*"] },
|
|
9
|
+
"lib": ["ES2022"],
|
|
10
|
+
"types": ["bun"],
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noUncheckedIndexedAccess": true,
|
|
13
|
+
"exactOptionalPropertyTypes": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"skipLibCheck": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*.ts", "tests/**/*.ts", "relkit.config.ts"],
|
|
20
|
+
"exclude": ["node_modules", ".relkit", "web"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { Providers } from "./providers";
|
|
3
|
+
|
|
4
|
+
export default function Layout({ children }: { readonly children: ReactNode }) {
|
|
5
|
+
return (
|
|
6
|
+
<html lang="en">
|
|
7
|
+
<body>
|
|
8
|
+
<Providers>{children}</Providers>
|
|
9
|
+
</body>
|
|
10
|
+
</html>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useAgent, useRoute } from "@relkit/client/react";
|
|
4
|
+
import { agentThreadIds } from "./agent-config";
|
|
5
|
+
|
|
6
|
+
function interruptQuestion(value: unknown): string {
|
|
7
|
+
if (typeof value === "string") return value;
|
|
8
|
+
if (
|
|
9
|
+
value !== null &&
|
|
10
|
+
typeof value === "object" &&
|
|
11
|
+
"question" in value &&
|
|
12
|
+
typeof value.question === "string"
|
|
13
|
+
) {
|
|
14
|
+
return value.question;
|
|
15
|
+
}
|
|
16
|
+
return "Approve?";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function Page() {
|
|
20
|
+
const greeting = useRoute("route.get.hello", { input: { name: "Relkit" } });
|
|
21
|
+
const assistant = useAgent("hello.assistant");
|
|
22
|
+
const review = useAgent("hello.review");
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<main>
|
|
26
|
+
<h1>{greeting.data?.message ?? "Loading…"}</h1>
|
|
27
|
+
<section>
|
|
28
|
+
<h2>Typed agent</h2>
|
|
29
|
+
<button
|
|
30
|
+
onClick={() => void assistant.send("Greet Ada", { threadId: agentThreadIds.assistant })}
|
|
31
|
+
>
|
|
32
|
+
Run assistant
|
|
33
|
+
</button>
|
|
34
|
+
<button onClick={() => void assistant.observe({ threadId: agentThreadIds.assistant })}>
|
|
35
|
+
Reconnect
|
|
36
|
+
</button>
|
|
37
|
+
<button onClick={() => void assistant.stop({ threadId: agentThreadIds.assistant })}>
|
|
38
|
+
Stop
|
|
39
|
+
</button>
|
|
40
|
+
<p>{assistant.output?.answer ?? assistant.status}</p>
|
|
41
|
+
{assistant.events.map((event) =>
|
|
42
|
+
event.kind === "custom" ? <p key={event.eventId}>{event.data.message}</p> : null,
|
|
43
|
+
)}
|
|
44
|
+
<ul>
|
|
45
|
+
{assistant.values?.todos.map((todo) => (
|
|
46
|
+
<li key={todo.content}>{`${todo.status}: ${todo.content}`}</li>
|
|
47
|
+
))}
|
|
48
|
+
{assistant.toolCalls.map((call) => (
|
|
49
|
+
<li key={call.toolCallId}>{`${call.toolId}: ${call.state}`}</li>
|
|
50
|
+
))}
|
|
51
|
+
</ul>
|
|
52
|
+
</section>
|
|
53
|
+
<section>
|
|
54
|
+
<h2>Interrupt and resume</h2>
|
|
55
|
+
<button
|
|
56
|
+
onClick={() =>
|
|
57
|
+
void review.run({ request: "Publish" }, { threadId: agentThreadIds.review })
|
|
58
|
+
}
|
|
59
|
+
>
|
|
60
|
+
Start review
|
|
61
|
+
</button>
|
|
62
|
+
{review.waiting ? (
|
|
63
|
+
<fieldset>
|
|
64
|
+
<legend>{interruptQuestion(review.waiting.requests[0]?.value)}</legend>
|
|
65
|
+
<button
|
|
66
|
+
onClick={() =>
|
|
67
|
+
void review.run(true, { threadId: agentThreadIds.review, resume: true })
|
|
68
|
+
}
|
|
69
|
+
>
|
|
70
|
+
Approve
|
|
71
|
+
</button>
|
|
72
|
+
<button
|
|
73
|
+
onClick={() =>
|
|
74
|
+
void review.run(false, { threadId: agentThreadIds.review, resume: true })
|
|
75
|
+
}
|
|
76
|
+
>
|
|
77
|
+
Reject
|
|
78
|
+
</button>
|
|
79
|
+
</fieldset>
|
|
80
|
+
) : null}
|
|
81
|
+
<p>{review.output?.result ?? review.status}</p>
|
|
82
|
+
</section>
|
|
83
|
+
</main>
|
|
84
|
+
);
|
|
85
|
+
}
|