create-windy 0.2.20 → 0.2.21

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.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +84 -10
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/README.md +1 -0
  6. package/template/apps/agent-server/Dockerfile +20 -0
  7. package/template/apps/agent-server/README.md +46 -0
  8. package/template/apps/agent-server/pyproject.toml +44 -0
  9. package/template/apps/agent-server/src/southwind_agent_server/__init__.py +5 -0
  10. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +39 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/app.py +263 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/config.py +141 -0
  13. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +99 -0
  14. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +52 -0
  15. package/template/apps/agent-server/src/southwind_agent_server/engine.py +85 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +197 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +246 -0
  18. package/template/apps/agent-server/src/southwind_agent_server/service.py +180 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +317 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/sse.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/store.py +144 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +80 -0
  23. package/template/apps/agent-server/tests/test_api.py +207 -0
  24. package/template/apps/agent-server/tests/test_config.py +56 -0
  25. package/template/apps/agent-server/tests/test_openai_provider.py +224 -0
  26. package/template/apps/agent-server/tests/test_sqlite_store.py +93 -0
  27. package/template/apps/agent-server/uv.lock +925 -0
  28. package/template/apps/server/src/agent/execution-grants.ts +89 -0
  29. package/template/apps/server/src/agent/remote-runtime.ts +128 -0
  30. package/template/apps/server/src/agent/run-contracts.ts +43 -0
  31. package/template/apps/server/src/agent/run-facade.test.ts +257 -0
  32. package/template/apps/server/src/agent/run-facade.ts +190 -0
  33. package/template/apps/server/src/agent/run-routes.ts +222 -0
  34. package/template/apps/server/src/agent/runtime-bootstrap.ts +53 -0
  35. package/template/apps/server/src/agent/tool-host.test.ts +242 -0
  36. package/template/apps/server/src/agent/tool-host.ts +153 -0
  37. package/template/apps/server/src/index.ts +12 -16
  38. package/template/apps/server/src/module-composition.test.ts +9 -0
  39. package/template/apps/server/src/module-composition.ts +13 -0
  40. package/template/apps/server/src/module-host/initialize-contracts.ts +67 -0
  41. package/template/apps/server/src/module-host/initialize.test.ts +69 -0
  42. package/template/apps/server/src/module-host/initialize.ts +50 -53
  43. package/template/apps/server/src/module-host.ts +1 -0
  44. package/template/apps/server/src/module-runtime-validation.ts +40 -0
  45. package/template/docker-compose.yml +25 -0
  46. package/template/docs/architecture/ai-runtime.md +744 -0
  47. package/template/docs/platform/agent-runtime.md +128 -0
  48. package/template/docs/platform/agent-tools.md +8 -1
  49. package/template/package.json +1 -0
  50. package/template/packages/config/index.test.ts +43 -0
  51. package/template/packages/config/index.ts +1 -0
  52. package/template/packages/config/package.json +2 -1
  53. package/template/packages/config/src/ai-openai-compatible.ts +131 -0
  54. package/template/packages/server-sdk/index.ts +11 -0
  55. package/template/packages/server-sdk/package.json +4 -2
  56. package/template/packages/server-sdk/src/ai-operation-registration.ts +31 -0
  57. package/template/packages/server-sdk/src/module-host.ts +5 -0
  58. package/template/packages/server-sdk/src/tool-host-port.ts +22 -0
@@ -21,6 +21,7 @@ import {
21
21
  createSystemToolHandlers,
22
22
  registerAgentRoutes,
23
23
  } from "./agent/routes.js";
24
+ import { registerAgentPlatformRuntime } from "./agent/runtime-bootstrap.js";
24
25
  // @windy-module system.agent end
25
26
  import { registerAuthRoutes, registerAuthSessionRoute } from "./auth/routes.js";
26
27
  import { createAuthRuntime } from "./auth/runtime.js";
@@ -55,7 +56,7 @@ import { registerNotificationRoutes } from "./notification/routes.js";
55
56
  import { createStorageRuntime } from "./storage/runtime.js";
56
57
  import { registerStorageRoutes } from "./storage/routes.js";
57
58
  // @windy-module system.storage end
58
- import { validateServerModuleComposition } from "./module-composition.js";
59
+ import { validateInstalledRuntime } from "./module-runtime-validation.js";
59
60
  import {
60
61
  adaptModuleSearchProviders,
61
62
  createModuleTaskDefinitions,
@@ -288,6 +289,14 @@ const toolHandlers = new Map([
288
289
  ...createModuleToolHandlerMap(moduleHosts),
289
290
  ]);
290
291
  registerAgentRoutes(app, foundationModules, toolHandlers, runtime);
292
+ registerAgentPlatformRuntime({
293
+ app,
294
+ platformConfig,
295
+ modules: foundationModules,
296
+ hosts: moduleHosts,
297
+ runtime,
298
+ toolHandlers,
299
+ });
291
300
  // @windy-module system.agent end
292
301
  // @windy-module system.scheduler begin
293
302
  registerSchedulerRoutes(app, scheduler, runtime, foundationSnapshot.features);
@@ -312,30 +321,17 @@ installModuleRoutes(moduleHosts, app, {
312
321
  });
313
322
  // @windy-module work-order end
314
323
 
315
- validateServerModuleComposition({
324
+ validateInstalledRuntime({
316
325
  modules: foundationModules,
317
326
  routes: app.routes,
318
327
  // @windy-module system.scheduler begin
319
328
  tasks: scheduledTaskDefinitions,
320
329
  // @windy-module system.scheduler end
321
- // @windy-module system.agent begin
322
330
  tools: toolHandlers,
323
- // @windy-module system.agent end
324
331
  // @windy-module system.search begin
325
332
  providers: searchHost.providerKeys,
326
333
  // @windy-module system.search end
327
- moduleRegistrations: {
328
- routes: moduleHosts.routes.map(
329
- ({ definition }) => `${definition.method} ${definition.path}`,
330
- ),
331
- tasks: moduleHosts.taskHandlers.map(
332
- ({ registration }) => registration.taskKey,
333
- ),
334
- tools: moduleHosts.toolHandlers.map(
335
- ({ registration }) => registration.toolKey,
336
- ),
337
- providers: moduleHosts.searchProviders.map(({ provider }) => provider.key),
338
- },
334
+ hosts: moduleHosts,
339
335
  });
340
336
 
341
337
  const port = process.env.PORT ? Number(process.env.PORT) : 9_747;
@@ -73,6 +73,15 @@ describe("Server Module Composition", () => {
73
73
  moduleRegistrations: { providers: ["system.unknown-search"] },
74
74
  }),
75
75
  ).toThrow("搜索 Provider已注册但未在 Manifest 声明:system.unknown-search");
76
+
77
+ expect(() =>
78
+ validateServerModuleComposition({
79
+ ...base,
80
+ moduleRegistrations: { aiOperations: ["system.unknown-operation"] },
81
+ }),
82
+ ).toThrow(
83
+ "AI Operation Handler已注册但未在 Manifest 声明:system.unknown-operation",
84
+ );
76
85
  });
77
86
  });
78
87
 
@@ -16,6 +16,7 @@ export interface ModuleImplementationRegistrations {
16
16
  routes?: readonly string[];
17
17
  tasks?: readonly string[];
18
18
  tools?: readonly string[];
19
+ aiOperations?: readonly string[];
19
20
  providers?: readonly string[];
20
21
  }
21
22
 
@@ -24,6 +25,7 @@ export interface ServerCompositionInput {
24
25
  routes: readonly RegisteredHttpRoute[];
25
26
  tasks?: readonly { key: string }[];
26
27
  tools?: ReadonlyMap<string, unknown>;
28
+ aiOperations?: readonly string[];
27
29
  providers?: readonly string[];
28
30
  moduleRegistrations?: ModuleImplementationRegistrations;
29
31
  }
@@ -35,6 +37,7 @@ export function validateServerModuleComposition(
35
37
  apiRoutes: input.routes.map(apiRouteKey),
36
38
  tasks: (input.tasks || []).map(({ key }) => key),
37
39
  tools: [...(input.tools?.keys() || [])],
40
+ aiOperations: input.aiOperations,
38
41
  providers: input.providers || [],
39
42
  });
40
43
  if (input.moduleRegistrations) {
@@ -55,6 +58,11 @@ function validateRegistrationConsistency(
55
58
  const declaredTools = new Set(
56
59
  modules.flatMap((module) => module.tools.map(({ key }) => key)),
57
60
  );
61
+ const declaredAiOperations = new Set(
62
+ modules.flatMap((module) =>
63
+ (module.aiOperations ?? []).map(({ key }) => key),
64
+ ),
65
+ );
58
66
  const declaredProviders = new Set(
59
67
  modules.flatMap((module) => module.providers.map(({ key }) => key)),
60
68
  );
@@ -62,6 +70,11 @@ function validateRegistrationConsistency(
62
70
  ["模块路由", registrations.routes || [], declaredRoutes],
63
71
  ["定时任务 Handler", registrations.tasks || [], declaredTasks],
64
72
  ["Agent Tool Handler", registrations.tools || [], declaredTools],
73
+ [
74
+ "AI Operation Handler",
75
+ registrations.aiOperations || [],
76
+ declaredAiOperations,
77
+ ],
65
78
  ["搜索 Provider", registrations.providers || [], declaredProviders],
66
79
  ] as const) {
67
80
  for (const key of registered) {
@@ -0,0 +1,67 @@
1
+ import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
2
+ import type { ModuleManifest } from "@southwind-ai/modules";
3
+ import type {
4
+ InstallableServerModule,
5
+ ModuleAiOperationHandlerRegistration,
6
+ ModuleInitializer,
7
+ ModuleRolePreset,
8
+ ModuleRouteDefinition,
9
+ ModuleSearchProvider,
10
+ ModuleTaskHandlerRegistration,
11
+ ModuleToolHandlerRegistration,
12
+ ModuleUploadPolicy,
13
+ } from "@southwind-ai/server-sdk";
14
+
15
+ export interface RegisteredModuleRoute {
16
+ readonly moduleName: string;
17
+ readonly definition: ModuleRouteDefinition;
18
+ }
19
+
20
+ export interface RegisteredModuleTaskHandler {
21
+ readonly moduleName: string;
22
+ readonly registration: ModuleTaskHandlerRegistration;
23
+ }
24
+
25
+ export interface RegisteredModuleToolHandler {
26
+ readonly moduleName: string;
27
+ readonly registration: ModuleToolHandlerRegistration;
28
+ }
29
+
30
+ export interface RegisteredModuleAiOperationHandler {
31
+ readonly moduleName: string;
32
+ readonly registration: ModuleAiOperationHandlerRegistration;
33
+ }
34
+
35
+ export interface RegisteredModuleRolePreset {
36
+ readonly moduleName: string;
37
+ readonly preset: ModuleRolePreset;
38
+ }
39
+
40
+ export interface RegisteredModuleSearchProvider {
41
+ readonly moduleName: string;
42
+ readonly provider: ModuleSearchProvider;
43
+ }
44
+
45
+ export interface InitializeModuleHostsInput {
46
+ modules: readonly ModuleManifest[];
47
+ /** Manifest 与 setup 由同一业务模块安装单元贡献。 */
48
+ installations?: readonly InstallableServerModule<ModuleManifest>[];
49
+ /** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
50
+ initializers?: readonly ModuleInitializer[];
51
+ baseUploadPolicies?: readonly ModuleUploadPolicy[];
52
+ }
53
+
54
+ export interface InitializedModuleHosts {
55
+ readonly manifests: readonly ModuleManifest[];
56
+ readonly uploadPolicies: readonly ModuleUploadPolicy[];
57
+ readonly routes: readonly RegisteredModuleRoute[];
58
+ readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
59
+ readonly toolHandlers: readonly RegisteredModuleToolHandler[];
60
+ readonly aiOperationHandlers: readonly RegisteredModuleAiOperationHandler[];
61
+ readonly rolePresets: readonly RegisteredModuleRolePreset[];
62
+ readonly searchProviders: readonly RegisteredModuleSearchProvider[];
63
+ manifestOf(moduleName: string): ModuleManifest;
64
+ /** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
65
+ uploadPurposesOf(moduleName: string): ReadonlySet<string>;
66
+ installDurableHandlers(registry: DurableHandlerRegistry): void;
67
+ }
@@ -102,6 +102,75 @@ describe("模块 Host 初始化", () => {
102
102
  }),
103
103
  ).rejects.toThrow("Server Module Manifest 与能力目录不是同一声明:known");
104
104
  });
105
+
106
+ test("AI Operation Handler 只注册本模块 Manifest 声明", async () => {
107
+ const operation = {
108
+ key: "known.summarize",
109
+ kind: "agent" as const,
110
+ featureKey: "known.ai",
111
+ permissionKey: "known.ai.execute",
112
+ auditAction: "known.ai.execute",
113
+ inputSchemaKey: "known.input.v1",
114
+ outputSchemaKey: "known.output.v1",
115
+ requiredCapabilities: ["text-generation"],
116
+ allowedToolKeys: [],
117
+ dataClassification: "internal" as const,
118
+ execution: "streaming" as const,
119
+ publicNetworkAccess: "forbidden" as const,
120
+ limits: {
121
+ maxDurationSeconds: 60,
122
+ maxInputBytes: 4096,
123
+ maxOutputBytes: 4096,
124
+ },
125
+ };
126
+ const known = manifest("known", { aiOperations: [operation] });
127
+ const initialized = await initializeModuleHosts({
128
+ modules: [known],
129
+ installations: [
130
+ {
131
+ manifest: known,
132
+ setup(host) {
133
+ host.registerAiOperationHandlers([
134
+ {
135
+ operationKey: operation.key,
136
+ prepare: () => ({
137
+ instructions: "按要求回答。",
138
+ userPrompt: "你好",
139
+ }),
140
+ },
141
+ ]);
142
+ },
143
+ },
144
+ ],
145
+ });
146
+ expect(
147
+ initialized.aiOperationHandlers.map(
148
+ ({ registration }) => registration.operationKey,
149
+ ),
150
+ ).toEqual(["known.summarize"]);
151
+
152
+ await expect(
153
+ initializeModuleHosts({
154
+ modules: [known],
155
+ installations: [
156
+ {
157
+ manifest: known,
158
+ setup(host) {
159
+ host.registerAiOperationHandlers([
160
+ {
161
+ operationKey: "known.unknown",
162
+ prepare: () => ({
163
+ instructions: "x",
164
+ userPrompt: "x",
165
+ }),
166
+ },
167
+ ]);
168
+ },
169
+ },
170
+ ],
171
+ }),
172
+ ).rejects.toThrow("AI Operation Handler 未在 Manifest 声明");
173
+ });
105
174
  });
106
175
 
107
176
  function initializer(
@@ -5,64 +5,31 @@ import {
5
5
  import type { ModuleManifest } from "@southwind-ai/modules";
6
6
  import {
7
7
  requireModuleNamespace,
8
- type InstallableServerModule,
9
8
  type ModuleHost,
10
- type ModuleInitializer,
11
- type ModuleRolePreset,
12
9
  type ModuleRouteDefinition,
13
- type ModuleSearchProvider,
14
- type ModuleTaskHandlerRegistration,
15
- type ModuleToolHandlerRegistration,
16
10
  type ModuleUploadPolicy,
17
11
  } from "@southwind-ai/server-sdk";
12
+ import type {
13
+ InitializedModuleHosts,
14
+ InitializeModuleHostsInput,
15
+ RegisteredModuleAiOperationHandler,
16
+ RegisteredModuleRolePreset,
17
+ RegisteredModuleRoute,
18
+ RegisteredModuleSearchProvider,
19
+ RegisteredModuleTaskHandler,
20
+ RegisteredModuleToolHandler,
21
+ } from "./initialize-contracts.js";
18
22
 
19
- export interface RegisteredModuleRoute {
20
- readonly moduleName: string;
21
- readonly definition: ModuleRouteDefinition;
22
- }
23
-
24
- export interface RegisteredModuleTaskHandler {
25
- readonly moduleName: string;
26
- readonly registration: ModuleTaskHandlerRegistration;
27
- }
28
-
29
- export interface RegisteredModuleToolHandler {
30
- readonly moduleName: string;
31
- readonly registration: ModuleToolHandlerRegistration;
32
- }
33
-
34
- export interface RegisteredModuleRolePreset {
35
- readonly moduleName: string;
36
- readonly preset: ModuleRolePreset;
37
- }
38
-
39
- export interface RegisteredModuleSearchProvider {
40
- readonly moduleName: string;
41
- readonly provider: ModuleSearchProvider;
42
- }
43
-
44
- export interface InitializeModuleHostsInput {
45
- modules: readonly ModuleManifest[];
46
- /** Manifest 与 setup 由同一业务模块安装单元贡献。 */
47
- installations?: readonly InstallableServerModule<ModuleManifest>[];
48
- /** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
49
- initializers?: readonly ModuleInitializer[];
50
- baseUploadPolicies?: readonly ModuleUploadPolicy[];
51
- }
52
-
53
- export interface InitializedModuleHosts {
54
- readonly manifests: readonly ModuleManifest[];
55
- readonly uploadPolicies: readonly ModuleUploadPolicy[];
56
- readonly routes: readonly RegisteredModuleRoute[];
57
- readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
58
- readonly toolHandlers: readonly RegisteredModuleToolHandler[];
59
- readonly rolePresets: readonly RegisteredModuleRolePreset[];
60
- readonly searchProviders: readonly RegisteredModuleSearchProvider[];
61
- manifestOf(moduleName: string): ModuleManifest;
62
- /** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
63
- uploadPurposesOf(moduleName: string): ReadonlySet<string>;
64
- installDurableHandlers(registry: DurableHandlerRegistry): void;
65
- }
23
+ export type {
24
+ InitializedModuleHosts,
25
+ InitializeModuleHostsInput,
26
+ RegisteredModuleAiOperationHandler,
27
+ RegisteredModuleRolePreset,
28
+ RegisteredModuleRoute,
29
+ RegisteredModuleSearchProvider,
30
+ RegisteredModuleTaskHandler,
31
+ RegisteredModuleToolHandler,
32
+ } from "./initialize-contracts.js";
66
33
 
67
34
  type HandlerInstaller = (registry: DurableHandlerRegistry) => void;
68
35
 
@@ -94,6 +61,8 @@ export async function initializeModuleHosts(
94
61
  taskKeys: new Set<string>(),
95
62
  toolHandlers: [],
96
63
  toolKeys: new Set<string>(),
64
+ aiOperationHandlers: [],
65
+ aiOperationKeys: new Set<string>(),
97
66
  rolePresets: [],
98
67
  rolePresetKeys: new Set<string>(),
99
68
  searchProviders: [],
@@ -142,6 +111,7 @@ export async function initializeModuleHosts(
142
111
  routes: [...state.routes],
143
112
  taskHandlers: [...state.taskHandlers],
144
113
  toolHandlers: [...state.toolHandlers],
114
+ aiOperationHandlers: [...state.aiOperationHandlers],
145
115
  rolePresets: [...state.rolePresets],
146
116
  searchProviders: [...state.searchProviders],
147
117
  manifestOf(moduleName) {
@@ -170,6 +140,8 @@ interface HostCollections {
170
140
  taskKeys: Set<string>;
171
141
  toolHandlers: RegisteredModuleToolHandler[];
172
142
  toolKeys: Set<string>;
143
+ aiOperationHandlers: RegisteredModuleAiOperationHandler[];
144
+ aiOperationKeys: Set<string>;
173
145
  rolePresets: RegisteredModuleRolePreset[];
174
146
  rolePresetKeys: Set<string>;
175
147
  searchProviders: RegisteredModuleSearchProvider[];
@@ -253,6 +225,31 @@ function createScopedHost(
253
225
  state.toolHandlers.push({ moduleName, registration });
254
226
  }
255
227
  },
228
+ registerAiOperationHandlers(registrations) {
229
+ for (const registration of registrations) {
230
+ requireModuleNamespace(
231
+ "AI Operation",
232
+ registration.operationKey,
233
+ moduleName,
234
+ );
235
+ if (
236
+ !manifest.aiOperations?.some(
237
+ ({ key }) => key === registration.operationKey,
238
+ )
239
+ ) {
240
+ throw new Error(
241
+ `AI Operation Handler 未在 Manifest 声明:${registration.operationKey}`,
242
+ );
243
+ }
244
+ if (state.aiOperationKeys.has(registration.operationKey)) {
245
+ throw new Error(
246
+ `AI Operation Handler 重复注册:${registration.operationKey}`,
247
+ );
248
+ }
249
+ state.aiOperationKeys.add(registration.operationKey);
250
+ state.aiOperationHandlers.push({ moduleName, registration });
251
+ }
252
+ },
256
253
  registerRolePresets(presets) {
257
254
  for (const preset of presets) {
258
255
  requireModuleNamespace("角色预设", preset.key, moduleName);
@@ -7,6 +7,7 @@ export {
7
7
  type RegisteredModuleSearchProvider,
8
8
  type RegisteredModuleTaskHandler,
9
9
  type RegisteredModuleToolHandler,
10
+ type RegisteredModuleAiOperationHandler,
10
11
  } from "./module-host/initialize.js";
11
12
  export {
12
13
  buildModuleRequestContext,
@@ -0,0 +1,40 @@
1
+ import type { ModuleManifest } from "@southwind-ai/modules";
2
+ import type { ModuleToolHandlers } from "./agent/routes.js";
3
+ import { validateServerModuleComposition } from "./module-composition.js";
4
+ import type { InitializedModuleHosts } from "./module-host.js";
5
+
6
+ export function validateInstalledRuntime(input: {
7
+ modules: ModuleManifest[];
8
+ routes: readonly { method: string; path: string }[];
9
+ tasks: readonly { key: string }[];
10
+ tools: ModuleToolHandlers;
11
+ providers: readonly string[];
12
+ hosts: InitializedModuleHosts;
13
+ }): void {
14
+ const aiOperations = input.hosts.aiOperationHandlers.map(
15
+ ({ registration }) => registration.operationKey,
16
+ );
17
+ validateServerModuleComposition({
18
+ modules: input.modules,
19
+ routes: input.routes,
20
+ tasks: input.tasks,
21
+ tools: input.tools,
22
+ aiOperations,
23
+ providers: input.providers,
24
+ moduleRegistrations: {
25
+ routes: input.hosts.routes.map(
26
+ ({ definition }) => `${definition.method} ${definition.path}`,
27
+ ),
28
+ tasks: input.hosts.taskHandlers.map(
29
+ ({ registration }) => registration.taskKey,
30
+ ),
31
+ tools: input.hosts.toolHandlers.map(
32
+ ({ registration }) => registration.toolKey,
33
+ ),
34
+ aiOperations,
35
+ providers: input.hosts.searchProviders.map(
36
+ ({ provider }) => provider.key,
37
+ ),
38
+ },
39
+ });
40
+ }
@@ -42,6 +42,10 @@ services:
42
42
  WINDY_CONTAINERIZED: "1"
43
43
  BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD: "true"
44
44
  WINDY_SYSTEM_USAGE_PATHS: /app/data/audit
45
+ PLATFORM_ENABLE_AI: ${PLATFORM_ENABLE_AI:-false}
46
+ AGENT_SERVER_URL: http://agent-server:8080
47
+ AGENT_TOOL_HOST_URL: http://server:9747
48
+ AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
45
49
  ports:
46
50
  - "9747:9747"
47
51
  volumes:
@@ -52,6 +56,26 @@ services:
52
56
  migrate:
53
57
  condition: service_completed_successfully
54
58
 
59
+ agent-server:
60
+ profiles: ["agent"]
61
+ build:
62
+ context: .
63
+ dockerfile: apps/agent-server/Dockerfile
64
+ environment:
65
+ AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
66
+ AGENT_RUN_STORE_PATH: /app/data/agent/runs.sqlite3
67
+ QWEN_API_URL: ${QWEN_API_URL:-}
68
+ QWEN_API_KEY: ${QWEN_API_KEY:-}
69
+ QWEN_MODEL: ${QWEN_MODEL:-}
70
+ AI_PROVIDER_CONNECT_TIMEOUT_MS: ${AI_PROVIDER_CONNECT_TIMEOUT_MS:-5000}
71
+ AI_PROVIDER_REQUEST_TIMEOUT_MS: ${AI_PROVIDER_REQUEST_TIMEOUT_MS:-60000}
72
+ AI_PROVIDER_MAX_RESPONSE_BYTES: ${AI_PROVIDER_MAX_RESPONSE_BYTES:-2097152}
73
+ volumes:
74
+ - agent-run-data:/app/data/agent
75
+ depends_on:
76
+ server:
77
+ condition: service_started
78
+
55
79
  web:
56
80
  build:
57
81
  context: .
@@ -85,3 +109,4 @@ services:
85
109
  volumes:
86
110
  postgres-data:
87
111
  server-audit-data:
112
+ agent-run-data: