@adminforth/agent 2.0.10 → 2.2.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.
Files changed (201) hide show
  1. package/README.md +333 -10
  2. package/dist/application/ports.d.ts +66 -0
  3. package/dist/application/runTurnUseCase.d.ts +71 -0
  4. package/dist/application/runTurnUseCase.js +364 -0
  5. package/dist/custom/ChatFooter.vue +58 -30
  6. package/dist/custom/SteerDebugPanel.vue +45 -0
  7. package/dist/custom/SteerQueue.vue +54 -0
  8. package/dist/custom/composables/agentStore/constants.ts +1 -0
  9. package/dist/custom/composables/agentStore/useAgentSessions.ts +35 -16
  10. package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +114 -0
  11. package/dist/custom/composables/useAgentStore.ts +46 -1
  12. package/dist/custom/conversation_area/MessageRenderer.vue +7 -1
  13. package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -1
  14. package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +3 -3
  15. package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +5 -1
  16. package/dist/domain/steerBuffer.d.ts +33 -0
  17. package/dist/domain/steerBuffer.js +51 -0
  18. package/dist/{agent → domain}/systemPrompt.js +1 -1
  19. package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
  20. package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
  21. package/dist/index.d.ts +4 -2
  22. package/dist/index.js +49 -35
  23. package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
  24. package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
  25. package/dist/llm/agentModels.js +65 -0
  26. package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +14 -2
  27. package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +41 -6
  28. package/dist/llm/langGraphLlm.d.ts +25 -0
  29. package/dist/llm/langGraphLlm.js +54 -0
  30. package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
  31. package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
  32. package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
  33. package/dist/llm/middleware/steerMiddleware.d.ts +21 -0
  34. package/dist/llm/middleware/steerMiddleware.js +50 -0
  35. package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
  36. package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
  37. package/dist/llm/streamAdapter.d.ts +18 -0
  38. package/dist/llm/streamAdapter.js +117 -0
  39. package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +19 -2
  40. package/dist/{sessionStore.js → persistence/sessionStore.js} +53 -0
  41. package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
  42. package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
  43. package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
  44. package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
  45. package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
  46. package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
  47. package/dist/{agent/tools → tools}/index.d.ts +1 -1
  48. package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
  49. package/dist/{endpoints → transport/http}/context.d.ts +12 -4
  50. package/dist/transport/http/context.js +1 -0
  51. package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +23 -3
  52. package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +36 -1
  53. package/dist/transport/sse/sseWriter.d.ts +29 -0
  54. package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +27 -0
  55. package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
  56. package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
  57. package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
  58. package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
  59. package/dist/types.d.ts +10 -4
  60. package/package.json +11 -2
  61. package/.woodpecker/buildRelease.sh +0 -13
  62. package/.woodpecker/buildSlackNotify.sh +0 -46
  63. package/.woodpecker/release.yml +0 -57
  64. package/agent/checkpointer.ts +0 -329
  65. package/agent/languageDetect.ts +0 -92
  66. package/agent/middleware/apiBasedTools.ts +0 -174
  67. package/agent/middleware/openAiResponsesContinuation.ts +0 -92
  68. package/agent/middleware/sequenceDebug.ts +0 -429
  69. package/agent/models/AgentModeResolver.ts +0 -9
  70. package/agent/models/AgentModelFactory.ts +0 -28
  71. package/agent/runtime/AgentContext.ts +0 -30
  72. package/agent/runtime/AgentRuntime.ts +0 -89
  73. package/agent/simpleAgent.ts +0 -199
  74. package/agent/skills/registry.ts +0 -162
  75. package/agent/speech/SpeechTurnService.ts +0 -179
  76. package/agent/systemPrompt.ts +0 -133
  77. package/agent/toolCallEvents.ts +0 -135
  78. package/agent/tools/AgentToolProvider.ts +0 -28
  79. package/agent/tools/apiTool.ts +0 -66
  80. package/agent/tools/fetchSkill.ts +0 -68
  81. package/agent/tools/fetchToolSchema.ts +0 -47
  82. package/agent/tools/getUserLocation.ts +0 -45
  83. package/agent/tools/index.ts +0 -31
  84. package/agent/tools/navigateUser.ts +0 -210
  85. package/agent/turn/TurnContextBuilder.ts +0 -36
  86. package/agent/turn/TurnLifecycleService.ts +0 -47
  87. package/agent/turn/TurnPersistenceService.ts +0 -33
  88. package/agent/turn/TurnPromptBuilder.ts +0 -51
  89. package/agent/turn/TurnStreamConsumer.ts +0 -69
  90. package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
  91. package/agent/turn/turnTypes.ts +0 -100
  92. package/agentEvents.ts +0 -75
  93. package/agentTurnService.ts +0 -314
  94. package/apiBasedTools.ts +0 -712
  95. package/build.log +0 -67
  96. package/chatSurfaceService.ts +0 -319
  97. package/custom/ChatFooter.vue +0 -173
  98. package/custom/ChatHeader.vue +0 -78
  99. package/custom/ChatSurface.vue +0 -160
  100. package/custom/CustomAutoScrollContainer.vue +0 -164
  101. package/custom/SessionsHistory.vue +0 -88
  102. package/custom/chat.ts +0 -82
  103. package/custom/composables/agentAudio/utils.ts +0 -220
  104. package/custom/composables/agentStore/constants.ts +0 -19
  105. package/custom/composables/agentStore/pageContext.ts +0 -8
  106. package/custom/composables/agentStore/useAgentChat.ts +0 -241
  107. package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
  108. package/custom/composables/agentStore/useAgentSessions.ts +0 -350
  109. package/custom/composables/useAgentAudio.ts +0 -272
  110. package/custom/composables/useAgentStore.ts +0 -510
  111. package/custom/composables/useAgentTransitions.ts +0 -58
  112. package/custom/conversation_area/ConversationArea.vue +0 -301
  113. package/custom/conversation_area/MessageRenderer.vue +0 -49
  114. package/custom/conversation_area/ProcessingTimeline.vue +0 -286
  115. package/custom/conversation_area/ReasoningRenderer.vue +0 -79
  116. package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
  117. package/custom/conversation_area/TextRenderer.vue +0 -182
  118. package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
  119. package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
  120. package/custom/conversation_area/ToolRenderer.vue +0 -178
  121. package/custom/conversation_area/ToolsGroup.vue +0 -59
  122. package/custom/env.d.ts +0 -7
  123. package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
  124. package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
  125. package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
  126. package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
  127. package/custom/package.json +0 -30
  128. package/custom/pnpm-lock.yaml +0 -2394
  129. package/custom/public/agentAudio/agent-processing.mp3 +0 -0
  130. package/custom/skills/analyze_data/SKILL.md +0 -25
  131. package/custom/skills/charts/SKILL.md +0 -193
  132. package/custom/skills/fetch_data/SKILL.md +0 -31
  133. package/custom/skills/mutate_data/SKILL.md +0 -121
  134. package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
  135. package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
  136. package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
  137. package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
  138. package/custom/tsconfig.json +0 -15
  139. package/custom/types.ts +0 -108
  140. package/custom/utils.ts +0 -38
  141. package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
  142. package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
  143. package/dist/agent/models/AgentModeResolver.d.ts +0 -9
  144. package/dist/agent/models/AgentModeResolver.js +0 -9
  145. package/dist/agent/simpleAgent.js +0 -118
  146. package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
  147. package/dist/agent/turn/TurnContextBuilder.js +0 -31
  148. package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
  149. package/dist/agent/turn/TurnLifecycleService.js +0 -47
  150. package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
  151. package/dist/agent/turn/TurnPersistenceService.js +0 -35
  152. package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
  153. package/dist/agent/turn/TurnPromptBuilder.js +0 -43
  154. package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
  155. package/dist/agent/turn/TurnStreamConsumer.js +0 -84
  156. package/dist/agentTurnService.d.ts +0 -24
  157. package/dist/agentTurnService.js +0 -269
  158. package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
  159. package/endpoints/chatSurfaces.ts +0 -66
  160. package/endpoints/context.ts +0 -52
  161. package/endpoints/core.ts +0 -181
  162. package/endpoints/sessions.ts +0 -183
  163. package/errors.ts +0 -10
  164. package/index.ts +0 -192
  165. package/pnpm-workspace.yaml +0 -4
  166. package/sanitizeSpeechText.ts +0 -32
  167. package/sessionStore.ts +0 -105
  168. package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
  169. package/tsconfig.json +0 -14
  170. package/types.ts +0 -127
  171. /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
  172. /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
  173. /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
  174. /package/dist/{agent → domain}/languageDetect.js +0 -0
  175. /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
  176. /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
  177. /package/dist/{agent → domain}/toolCallEvents.js +0 -0
  178. /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
  179. /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
  180. /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
  181. /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
  182. /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
  183. /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
  184. /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
  185. /package/dist/{errors.js → shared/errors.js} +0 -0
  186. /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
  187. /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
  188. /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
  189. /package/dist/{agent/tools → tools}/apiTool.js +0 -0
  190. /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
  191. /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
  192. /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
  193. /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
  194. /package/dist/{agent/tools → tools}/index.js +0 -0
  195. /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
  196. /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
  197. /package/dist/{agent → tools}/skills/registry.js +0 -0
  198. /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
  199. /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
  200. /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
  201. /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
@@ -8,8 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { logger } from "adminforth";
11
- import { getErrorMessage, isAbortError } from "../../errors.js";
12
- import { sanitizeSpeechText } from "../../sanitizeSpeechText.js";
11
+ import { getErrorMessage, isAbortError } from "../../shared/errors.js";
12
+ import { sanitizeSpeechText } from "../../shared/sanitizeSpeechText.js";
13
13
  export class SpeechTurnService {
14
14
  constructor(runAndPersistAgentResponse) {
15
15
  this.runAndPersistAgentResponse = runAndPersistAgentResponse;
package/dist/types.d.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  import { type PluginsCommonOptions, type AdminUser, type HttpExtra, type AudioAdapter, type ChatSurfaceAdapter } from "adminforth";
2
- import type { AgentModeCompletionAdapter } from "./agent/simpleAgent.js";
2
+ import type { AgentModeCompletionAdapter } from "./application/ports.js";
3
3
  interface ISessionResource {
4
4
  resourceId: string;
5
5
  idField: string;
6
6
  titleField: string;
7
- turnsField: string;
7
+ /**
8
+ * @deprecated Not used by the plugin — session turns are looked up via
9
+ * `turnResource.sessionIdField`. Kept optional for backward compatibility;
10
+ * will be removed in a future major version.
11
+ */
12
+ turnsField?: string;
8
13
  askerIdField: string;
9
14
  createdAtField: string;
10
15
  }
@@ -69,8 +74,9 @@ export interface PluginOptions extends PluginsCommonOptions {
69
74
  */
70
75
  systemPrompt?: string;
71
76
  /**
72
- * Response generation level.
73
- * Default is low
77
+ * @deprecated Not applied — reasoning effort is configured on the completion
78
+ * adapter (e.g. `extraRequestBodyParameters.reasoning.effort`), not here.
79
+ * Kept for backward compatibility; will be removed in a future major version.
74
80
  */
75
81
  reasoning?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
76
82
  /**
package/package.json CHANGED
@@ -1,15 +1,21 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "2.0.10",
3
+ "version": "2.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
7
12
  "homepage": "https://adminforth.dev/docs/tutorial/Plugins/agent/",
8
13
  "publishConfig": {
9
14
  "access": "public"
10
15
  },
11
16
  "scripts": {
12
- "build": "tsc && rsync -av --exclude 'node_modules' custom dist/"
17
+ "build": "tsc && rsync -av --exclude 'node_modules' custom dist/",
18
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --runInBand"
13
19
  },
14
20
  "keywords": [
15
21
  "adminforth",
@@ -24,10 +30,13 @@
24
30
  "license": "MIT",
25
31
  "description": "AI agent plugin for AdminForth with tool-based workflows and persistent chat sessions",
26
32
  "devDependencies": {
33
+ "@types/jest": "^30.0.0",
27
34
  "@types/node": "latest",
28
35
  "adminforth": "^3.8.2",
36
+ "jest": "^30.3.0",
29
37
  "semantic-release": "^24.2.1",
30
38
  "semantic-release-slack-bot": "^4.0.2",
39
+ "ts-jest": "^29.4.6",
31
40
  "typescript": "^5.7.3"
32
41
  },
33
42
  "dependencies": {
@@ -1,13 +0,0 @@
1
-
2
- #!/bin/bash
3
-
4
- # write npm run output both to console and to build.log
5
- npm run build 2>&1 | tee build.log
6
- build_status=${PIPESTATUS[0]}
7
-
8
- # if exist status from the npm run build is not 0
9
- # then exit with the status code from the npm run build
10
- if [ $build_status -ne 0 ]; then
11
- echo "Build failed. Exiting with status code $build_status"
12
- exit $build_status
13
- fi
@@ -1,46 +0,0 @@
1
- #!/bin/sh
2
-
3
- set -x
4
-
5
- COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8)
6
-
7
- STATUS=${1}
8
-
9
-
10
- if [ "$STATUS" = "success" ]; then
11
- MESSAGE="Did a build without issues on \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\`. Commit: _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
12
-
13
- curl -s -X POST -H "Content-Type: application/json" -d '{
14
- "username": "'"$CI_COMMIT_AUTHOR"'",
15
- "icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
16
- "attachments": [
17
- {
18
- "mrkdwn_in": ["text", "pretext"],
19
- "color": "#36a64f",
20
- "text": "'"$MESSAGE"'"
21
- }
22
- ]
23
- }' "$DEVELOPERS_SLACK_WEBHOOK"
24
- exit 0
25
- fi
26
- export BUILD_LOG=$(cat ./build.log)
27
-
28
- BUILD_LOG=$(echo $BUILD_LOG | sed 's/"/\\"/g')
29
-
30
- MESSAGE="Broke \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\` with commit _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
31
- CODE_BLOCK="\`\`\`$BUILD_LOG\n\`\`\`"
32
-
33
- echo "Sending slack message to developers $MESSAGE"
34
- # Send the message
35
- curl -sS -X POST -H "Content-Type: application/json" -d '{
36
- "username": "'"$CI_COMMIT_AUTHOR"'",
37
- "icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
38
- "attachments": [
39
- {
40
- "mrkdwn_in": ["text", "pretext"],
41
- "color": "#8A1C12",
42
- "text": "'"$CODE_BLOCK"'",
43
- "pretext": "'"$MESSAGE"'"
44
- }
45
- ]
46
- }' "$DEVELOPERS_SLACK_WEBHOOK" 2>&1
@@ -1,57 +0,0 @@
1
- clone:
2
- git:
3
- image: woodpeckerci/plugin-git
4
- settings:
5
- partial: false
6
- depth: 5
7
-
8
- steps:
9
- init-secrets:
10
- when:
11
- - event: push
12
- image: infisical/cli
13
- environment:
14
- INFISICAL_TOKEN:
15
- from_secret: VAULT_TOKEN
16
- commands:
17
- - infisical export --domain https://vault.devforth.io/api --format=dotenv-export --env="prod" > /woodpecker/deploy.vault.env
18
-
19
- build:
20
- image: devforth/node20-pnpm:latest
21
- when:
22
- - event: push
23
- commands:
24
- - apt update && apt install -y rsync
25
- - . /woodpecker/deploy.vault.env
26
- - pnpm install
27
- - /bin/bash ./.woodpecker/buildRelease.sh
28
- - npm audit signatures
29
-
30
- release:
31
- image: devforth/node20-pnpm:latest
32
- when:
33
- - event:
34
- - push
35
- branch:
36
- - main
37
- commands:
38
- - . /woodpecker/deploy.vault.env
39
- - pnpm exec semantic-release
40
-
41
- slack-on-failure:
42
- image: curlimages/curl
43
- when:
44
- - event: push
45
- status: [failure]
46
- commands:
47
- - . /woodpecker/deploy.vault.env
48
- - /bin/sh ./.woodpecker/buildSlackNotify.sh failure
49
-
50
- slack-on-success:
51
- image: curlimages/curl
52
- when:
53
- - event: push
54
- status: [success]
55
- commands:
56
- - . /woodpecker/deploy.vault.env
57
- - /bin/sh ./.woodpecker/buildSlackNotify.sh success
@@ -1,329 +0,0 @@
1
- import type { RunnableConfig } from "@langchain/core/runnables";
2
- import {
3
- BaseCheckpointSaver,
4
- type Checkpoint,
5
- type CheckpointPendingWrite,
6
- type CheckpointMetadata,
7
- type CheckpointTuple,
8
- type PendingWrite,
9
- WRITES_IDX_MAP,
10
- } from "@langchain/langgraph-checkpoint";
11
- import type { PluginOptions } from "../types.js";
12
- import { Filters } from "adminforth";
13
-
14
- const ROOT_CHECKPOINT_NAMESPACE = "__root__";
15
- type CheckpointRow = Record<string, unknown>;
16
-
17
- export class AdminForthCheckpointSaver extends BaseCheckpointSaver {
18
- constructor(
19
- private readonly adminforth: any,
20
- private readonly pluginOptions: PluginOptions,
21
- ) {
22
- super();
23
- }
24
-
25
- private get resourceConfig() {
26
- const resource = this.pluginOptions.checkpointResource;
27
- if (!resource) {
28
- throw new Error("checkpointResource is not configured");
29
- }
30
- return resource;
31
- }
32
-
33
- private resource() {
34
- return this.adminforth.resource(this.resourceConfig.resourceId);
35
- }
36
-
37
- private serialize(value: unknown): string | null {
38
- if (value === undefined || value === null) return null;
39
- return JSON.stringify(value);
40
- }
41
-
42
- private deserialize<T>(value: unknown): T | null {
43
- if (value === undefined || value === null) return null;
44
- if (typeof value === "string") {
45
- return JSON.parse(value) as T;
46
- }
47
- return value as T;
48
- }
49
-
50
- private now(): string {
51
- return new Date().toISOString();
52
- }
53
-
54
- private encodeCheckpointNamespace(checkpointNs: string): string {
55
- return checkpointNs === "" ? ROOT_CHECKPOINT_NAMESPACE : checkpointNs;
56
- }
57
-
58
- private decodeCheckpointNamespace(checkpointNs: unknown): string {
59
- return checkpointNs === ROOT_CHECKPOINT_NAMESPACE ? "" : String(checkpointNs ?? "");
60
- }
61
-
62
- private getConfigValues(config: RunnableConfig) {
63
- const configurable = (config.configurable ?? {}) as Record<string, unknown>;
64
-
65
- return {
66
- threadId: String(configurable.thread_id ?? ""),
67
- checkpointNs: String(configurable.checkpoint_ns ?? ""),
68
- checkpointId: configurable.checkpoint_id
69
- ? String(configurable.checkpoint_id)
70
- : null,
71
- };
72
- }
73
-
74
- private buildConfig(threadId: string, checkpointNs: string, checkpointId: string): RunnableConfig {
75
- return {
76
- configurable: {
77
- thread_id: threadId,
78
- checkpoint_ns: checkpointNs,
79
- checkpoint_id: checkpointId,
80
- },
81
- };
82
- }
83
-
84
- private buildCheckpointRowId(threadId: string, checkpointNs: string, checkpointId: string) {
85
- return `cp:${threadId}:${checkpointNs}:${checkpointId}`;
86
- }
87
-
88
- private buildWritesRowId(
89
- threadId: string,
90
- checkpointNs: string,
91
- checkpointId: string,
92
- taskId: string,
93
- seq: number,
94
- ) {
95
- return `wr:${threadId}:${checkpointNs}:${checkpointId}:${taskId}:${seq}`;
96
- }
97
-
98
- private getWriteIndex(channel: string, index: number): number {
99
- return WRITES_IDX_MAP[channel] ?? index;
100
- }
101
-
102
- private isDuplicateCheckpointWriteError(error: unknown): boolean {
103
- return error instanceof Error && error.message.includes("UNIQUE constraint failed");
104
- }
105
-
106
- async put(
107
- config: RunnableConfig,
108
- checkpoint: Checkpoint,
109
- metadata: CheckpointMetadata,
110
- _newVersions: Record<string, unknown>,
111
- ): Promise<RunnableConfig> {
112
- const r = this.resourceConfig;
113
- const { threadId, checkpointNs } = this.getConfigValues(config);
114
- const checkpointId = String((checkpoint as any).id);
115
- const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
116
-
117
- const parentCheckpointId = this.getConfigValues(config).checkpointId;
118
- const createdAt = this.now();
119
-
120
- await this.resource().create({
121
- [r.idField]: this.buildCheckpointRowId(threadId, storedCheckpointNs, checkpointId),
122
- [r.threadIdField]: threadId,
123
- [r.checkpointNamespaceField]: storedCheckpointNs,
124
- [r.checkpointIdField]: checkpointId,
125
- [r.parentCheckpointIdField]: parentCheckpointId,
126
- [r.rowKindField]: "checkpoint",
127
- [r.taskIdField]: null,
128
- [r.sequenceField]: 0,
129
- [r.createdAtField]: createdAt,
130
- [r.checkpointPayloadField]: this.serialize(checkpoint),
131
- [r.metadataPayloadField]: this.serialize(metadata),
132
- [r.writesPayloadField]: null,
133
- [r.schemaVersionField]: 1,
134
- });
135
-
136
- return this.buildConfig(threadId, checkpointNs, checkpointId);
137
- }
138
-
139
- async putWrites(
140
- config: RunnableConfig,
141
- writes: PendingWrite[],
142
- taskId: string,
143
- ): Promise<void> {
144
- const r = this.resourceConfig;
145
- const { threadId, checkpointNs, checkpointId } = this.getConfigValues(config);
146
- const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
147
-
148
- if (!checkpointId) {
149
- throw new Error("putWrites requires checkpoint_id in config");
150
- }
151
-
152
- const createdAt = this.now();
153
-
154
- await Promise.all(
155
- writes.map(async ([channel, value], index) => {
156
- const writeIndex = this.getWriteIndex(channel, index);
157
-
158
- try {
159
- await this.resource().create({
160
- [r.idField]: this.buildWritesRowId(
161
- threadId,
162
- storedCheckpointNs,
163
- checkpointId,
164
- taskId,
165
- writeIndex,
166
- ),
167
- [r.threadIdField]: threadId,
168
- [r.checkpointNamespaceField]: storedCheckpointNs,
169
- [r.checkpointIdField]: checkpointId,
170
- [r.parentCheckpointIdField]: null,
171
- [r.rowKindField]: "writes",
172
- [r.taskIdField]: taskId,
173
- [r.sequenceField]: writeIndex,
174
- [r.createdAtField]: createdAt,
175
- [r.checkpointPayloadField]: null,
176
- [r.metadataPayloadField]: null,
177
- [r.writesPayloadField]: this.serialize([channel, value] satisfies PendingWrite),
178
- [r.schemaVersionField]: 1,
179
- });
180
- } catch (error) {
181
- if (!this.isDuplicateCheckpointWriteError(error)) {
182
- throw error;
183
- }
184
- }
185
- }),
186
- );
187
- }
188
-
189
- async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {
190
- const r = this.resourceConfig;
191
- const { threadId, checkpointNs, checkpointId } = this.getConfigValues(config);
192
- const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
193
-
194
- const checkpointRows = await this.resource().list(
195
- checkpointId
196
- ? Filters.AND(
197
- Filters.EQ(r.threadIdField, threadId),
198
- Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
199
- Filters.EQ(r.checkpointIdField, checkpointId),
200
- Filters.EQ(r.rowKindField, "checkpoint"),
201
- )
202
- : Filters.AND(
203
- Filters.EQ(r.threadIdField, threadId),
204
- Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
205
- Filters.EQ(r.rowKindField, "checkpoint"),
206
- ),
207
- 1,
208
- undefined,
209
- [{ field: r.checkpointIdField, direction: "desc" }],
210
- );
211
-
212
- const checkpointRow = checkpointRows[0];
213
- if (!checkpointRow) {
214
- return undefined;
215
- }
216
-
217
- const resolvedCheckpointId = String(checkpointRow[r.checkpointIdField]);
218
-
219
- const writesRows = await this.resource().list(
220
- Filters.AND(
221
- Filters.EQ(r.threadIdField, threadId),
222
- Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
223
- Filters.EQ(r.checkpointIdField, resolvedCheckpointId),
224
- Filters.EQ(r.rowKindField, "writes"),
225
- ),
226
- undefined,
227
- undefined,
228
- [{ field: r.sequenceField, direction: "asc" }],
229
- );
230
-
231
- const pendingWrites: CheckpointPendingWrite[] = writesRows.flatMap((row: CheckpointRow) => {
232
- const taskId = String(row[r.taskIdField] ?? "");
233
- const write = this.deserialize<PendingWrite>(row[r.writesPayloadField]);
234
- if (!write) {
235
- return [];
236
- }
237
-
238
- const [channel, value] = write;
239
- return [[taskId, channel, value]];
240
- });
241
-
242
- const parentCheckpointId = checkpointRow[r.parentCheckpointIdField]
243
- ? String(checkpointRow[r.parentCheckpointIdField])
244
- : null;
245
-
246
- const tuple: CheckpointTuple = {
247
- config: this.buildConfig(threadId, checkpointNs, resolvedCheckpointId),
248
- checkpoint: this.deserialize<Checkpoint>(
249
- checkpointRow[r.checkpointPayloadField],
250
- ) as Checkpoint,
251
- metadata: (this.deserialize<CheckpointMetadata>(
252
- checkpointRow[r.metadataPayloadField],
253
- ) ?? {}) as CheckpointMetadata,
254
- parentConfig: parentCheckpointId
255
- ? this.buildConfig(threadId, checkpointNs, parentCheckpointId)
256
- : undefined,
257
- pendingWrites,
258
- };
259
-
260
- return tuple;
261
- }
262
-
263
- async *list(
264
- config: RunnableConfig,
265
- options?: {
266
- before?: RunnableConfig;
267
- limit?: number;
268
- },
269
- ): AsyncGenerator<CheckpointTuple> {
270
- const r = this.resourceConfig;
271
- const { threadId, checkpointNs } = this.getConfigValues(config);
272
- const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
273
- const beforeCheckpointId = options?.before
274
- ? this.getConfigValues(options.before).checkpointId
275
- : null;
276
-
277
- const filters: Filters[] = [
278
- Filters.EQ(r.rowKindField, "checkpoint"),
279
- Filters.EQ(r.threadIdField, threadId),
280
- Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
281
- ];
282
-
283
- if (beforeCheckpointId) {
284
- filters.push(Filters.LT(r.checkpointIdField, beforeCheckpointId));
285
- }
286
-
287
- const rows = await this.resource().list(
288
- Filters.AND(...filters),
289
- options?.limit,
290
- undefined,
291
- [{ field: r.checkpointIdField, direction: "desc" }],
292
- );
293
-
294
- for (const row of rows) {
295
- const tuple = await this.getTuple(
296
- this.buildConfig(
297
- String(row[r.threadIdField]),
298
- this.decodeCheckpointNamespace(row[r.checkpointNamespaceField]),
299
- String(row[r.checkpointIdField]),
300
- ),
301
- );
302
-
303
- if (tuple) {
304
- yield tuple;
305
- }
306
- }
307
- }
308
-
309
- async deleteThread(threadId: string, checkpointNs = ""): Promise<void> {
310
- const r = this.resourceConfig;
311
- const storedCheckpointNs = this.encodeCheckpointNamespace(checkpointNs);
312
-
313
- const rows = await this.resource().list(
314
- Filters.AND(
315
- Filters.EQ(r.threadIdField, threadId),
316
- Filters.EQ(r.checkpointNamespaceField, storedCheckpointNs),
317
- ),
318
- undefined,
319
- undefined,
320
- [{ field: r.createdAtField, direction: "desc" }],
321
- );
322
-
323
- for (const row of rows) {
324
- await this.adminforth
325
- .resource(this.pluginOptions.checkpointResource!.resourceId)
326
- .delete(row[r.idField]);
327
- }
328
- }
329
- }
@@ -1,92 +0,0 @@
1
- import { logger } from "adminforth";
2
- import type { PluginOptions } from "../types.js";
3
-
4
- export type DetectedLanguage = {
5
- language: string;
6
- code: string; // ISO 639-1
7
- ambiguous: boolean;
8
- };
9
-
10
- export type PreviousUserMessage = {
11
- text: string;
12
- };
13
-
14
- const USER_LANGUAGE_OUTPUT_SCHEMA = {
15
- name: "user_language",
16
- strict: true,
17
- schema: {
18
- type: "object",
19
- additionalProperties: false,
20
- properties: {
21
- language: {
22
- type: "string",
23
- description: "Full English language name, for example English, Ukrainian, French.",
24
- },
25
- code: {
26
- type: "string",
27
- description: "Uppercase ISO 639-1 two-letter language code, for example EN, UK, FR.",
28
- },
29
- ambiguous: {
30
- type: "boolean",
31
- description: "True if the user's language cannot be confidently detected from the message.",
32
- },
33
- },
34
- required: ["language", "code", "ambiguous"],
35
- },
36
- } as const;
37
-
38
- function parseUserLanguage(content: string | undefined): DetectedLanguage | null {
39
- if (!content) {
40
- return null;
41
- }
42
-
43
- try {
44
- const parsed = JSON.parse(content) as DetectedLanguage;
45
- return {
46
- language: parsed.language,
47
- code: parsed.code,
48
- ambiguous: parsed.ambiguous,
49
- };
50
- } catch (error) {
51
- logger.warn(`Failed to parse detected user language: ${error instanceof Error ? error.message : String(error)}`);
52
- return null;
53
- }
54
- }
55
-
56
- export async function detectUserLanguage(
57
- completionAdapter: PluginOptions["modes"][number]["completionAdapter"],
58
- prompt: string,
59
- previousUserMessages: PreviousUserMessage[] = [],
60
- ): Promise<DetectedLanguage | null> {
61
- const previousMessages = previousUserMessages.length
62
- ? [
63
- "",
64
- "Previous user messages:",
65
- ...previousUserMessages.map((message) => message.text),
66
- ]
67
- : [];
68
- const response = await completionAdapter.complete({
69
- content: [
70
- "Detect the language the assistant should use for the current user message.",
71
- "Use recent conversation context only to resolve short or ambiguous current messages.",
72
- "Return only the requested structured output.",
73
- "The language must be the full English language name.",
74
- "The code must be an uppercase ISO 639-1 two-letter code like UK, EN, FR.",
75
- "Set ambiguous to true if the response language cannot be confidently detected from the current message or context.",
76
- ...previousMessages,
77
- "",
78
- "Current user message:",
79
- prompt,
80
- ].join("\n"),
81
- maxTokens: 80,
82
- outputSchema: USER_LANGUAGE_OUTPUT_SCHEMA,
83
- reasoningEffort: "none",
84
- });
85
-
86
- if (response.error) {
87
- logger.warn(`Failed to detect user language: ${response.error}`);
88
- return null;
89
- }
90
-
91
- return parseUserLanguage(response.content);
92
- }