@adminforth/agent 2.0.10 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +333 -10
- package/dist/application/ports.d.ts +66 -0
- package/dist/application/runTurnUseCase.d.ts +68 -0
- package/dist/application/runTurnUseCase.js +358 -0
- package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +1 -1
- package/dist/{agent → domain}/systemPrompt.js +1 -1
- package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
- package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +40 -35
- package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
- package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
- package/dist/llm/agentModels.js +65 -0
- package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +11 -2
- package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +38 -6
- package/dist/llm/langGraphLlm.d.ts +25 -0
- package/dist/llm/langGraphLlm.js +54 -0
- package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
- package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
- package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
- package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
- package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
- package/dist/llm/streamAdapter.d.ts +18 -0
- package/dist/llm/streamAdapter.js +117 -0
- package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +12 -2
- package/dist/{sessionStore.js → persistence/sessionStore.js} +33 -0
- package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
- package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
- package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
- package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
- package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
- package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
- package/dist/{agent/tools → tools}/index.d.ts +1 -1
- package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
- package/dist/{endpoints → transport/http}/context.d.ts +2 -2
- package/dist/transport/http/context.js +1 -0
- package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +3 -3
- package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +7 -1
- package/dist/transport/sse/sseWriter.d.ts +29 -0
- package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +15 -0
- package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
- package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
- package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
- package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
- package/dist/types.d.ts +10 -4
- package/package.json +11 -2
- package/.woodpecker/buildRelease.sh +0 -13
- package/.woodpecker/buildSlackNotify.sh +0 -46
- package/.woodpecker/release.yml +0 -57
- package/agent/checkpointer.ts +0 -329
- package/agent/languageDetect.ts +0 -92
- package/agent/middleware/apiBasedTools.ts +0 -174
- package/agent/middleware/openAiResponsesContinuation.ts +0 -92
- package/agent/middleware/sequenceDebug.ts +0 -429
- package/agent/models/AgentModeResolver.ts +0 -9
- package/agent/models/AgentModelFactory.ts +0 -28
- package/agent/runtime/AgentContext.ts +0 -30
- package/agent/runtime/AgentRuntime.ts +0 -89
- package/agent/simpleAgent.ts +0 -199
- package/agent/skills/registry.ts +0 -162
- package/agent/speech/SpeechTurnService.ts +0 -179
- package/agent/systemPrompt.ts +0 -133
- package/agent/toolCallEvents.ts +0 -135
- package/agent/tools/AgentToolProvider.ts +0 -28
- package/agent/tools/apiTool.ts +0 -66
- package/agent/tools/fetchSkill.ts +0 -68
- package/agent/tools/fetchToolSchema.ts +0 -47
- package/agent/tools/getUserLocation.ts +0 -45
- package/agent/tools/index.ts +0 -31
- package/agent/tools/navigateUser.ts +0 -210
- package/agent/turn/TurnContextBuilder.ts +0 -36
- package/agent/turn/TurnLifecycleService.ts +0 -47
- package/agent/turn/TurnPersistenceService.ts +0 -33
- package/agent/turn/TurnPromptBuilder.ts +0 -51
- package/agent/turn/TurnStreamConsumer.ts +0 -69
- package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
- package/agent/turn/turnTypes.ts +0 -100
- package/agentEvents.ts +0 -75
- package/agentTurnService.ts +0 -314
- package/apiBasedTools.ts +0 -712
- package/build.log +0 -67
- package/chatSurfaceService.ts +0 -319
- package/custom/ChatFooter.vue +0 -173
- package/custom/ChatHeader.vue +0 -78
- package/custom/ChatSurface.vue +0 -160
- package/custom/CustomAutoScrollContainer.vue +0 -164
- package/custom/SessionsHistory.vue +0 -88
- package/custom/chat.ts +0 -82
- package/custom/composables/agentAudio/utils.ts +0 -220
- package/custom/composables/agentStore/constants.ts +0 -19
- package/custom/composables/agentStore/pageContext.ts +0 -8
- package/custom/composables/agentStore/useAgentChat.ts +0 -241
- package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
- package/custom/composables/agentStore/useAgentSessions.ts +0 -350
- package/custom/composables/useAgentAudio.ts +0 -272
- package/custom/composables/useAgentStore.ts +0 -510
- package/custom/composables/useAgentTransitions.ts +0 -58
- package/custom/conversation_area/ConversationArea.vue +0 -301
- package/custom/conversation_area/MessageRenderer.vue +0 -49
- package/custom/conversation_area/ProcessingTimeline.vue +0 -286
- package/custom/conversation_area/ReasoningRenderer.vue +0 -79
- package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
- package/custom/conversation_area/TextRenderer.vue +0 -182
- package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
- package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
- package/custom/conversation_area/ToolRenderer.vue +0 -178
- package/custom/conversation_area/ToolsGroup.vue +0 -59
- package/custom/env.d.ts +0 -7
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
- package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
- package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
- package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
- package/custom/package.json +0 -30
- package/custom/pnpm-lock.yaml +0 -2394
- package/custom/public/agentAudio/agent-processing.mp3 +0 -0
- package/custom/skills/analyze_data/SKILL.md +0 -25
- package/custom/skills/charts/SKILL.md +0 -193
- package/custom/skills/fetch_data/SKILL.md +0 -31
- package/custom/skills/mutate_data/SKILL.md +0 -121
- package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
- package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
- package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
- package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
- package/custom/tsconfig.json +0 -15
- package/custom/types.ts +0 -108
- package/custom/utils.ts +0 -38
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
- package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
- package/dist/agent/models/AgentModeResolver.d.ts +0 -9
- package/dist/agent/models/AgentModeResolver.js +0 -9
- package/dist/agent/simpleAgent.js +0 -118
- package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
- package/dist/agent/turn/TurnContextBuilder.js +0 -31
- package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
- package/dist/agent/turn/TurnLifecycleService.js +0 -47
- package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
- package/dist/agent/turn/TurnPersistenceService.js +0 -35
- package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
- package/dist/agent/turn/TurnPromptBuilder.js +0 -43
- package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
- package/dist/agent/turn/TurnStreamConsumer.js +0 -84
- package/dist/agentTurnService.d.ts +0 -24
- package/dist/agentTurnService.js +0 -269
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
- package/endpoints/chatSurfaces.ts +0 -66
- package/endpoints/context.ts +0 -52
- package/endpoints/core.ts +0 -181
- package/endpoints/sessions.ts +0 -183
- package/errors.ts +0 -10
- package/index.ts +0 -192
- package/pnpm-workspace.yaml +0 -4
- package/sanitizeSpeechText.ts +0 -32
- package/sessionStore.ts +0 -105
- package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
- package/tsconfig.json +0 -14
- package/types.ts +0 -127
- /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
- /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
- /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
- /package/dist/{agent → domain}/languageDetect.js +0 -0
- /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.js +0 -0
- /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
- /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
- /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
- /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
- /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
- /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
- /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
- /package/dist/{errors.js → shared/errors.js} +0 -0
- /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
- /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
- /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
- /package/dist/{agent/tools → tools}/apiTool.js +0 -0
- /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
- /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
- /package/dist/{agent/tools → tools}/index.js +0 -0
- /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
- /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
- /package/dist/{agent → tools}/skills/registry.js +0 -0
- /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
- /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
package/agent/checkpointer.ts
DELETED
|
@@ -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
|
-
}
|
package/agent/languageDetect.ts
DELETED
|
@@ -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
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { ToolMessage } from "@langchain/core/messages";
|
|
2
|
-
import { isGraphInterrupt } from "@langchain/langgraph";
|
|
3
|
-
import { createMiddleware } from "langchain";
|
|
4
|
-
import { logger, type AdminUser, type IAdminForth } from "adminforth";
|
|
5
|
-
import {
|
|
6
|
-
formatApiBasedToolCall,
|
|
7
|
-
type ApiBasedTool,
|
|
8
|
-
} from "../../apiBasedTools.js";
|
|
9
|
-
import {
|
|
10
|
-
createToolCallTracker,
|
|
11
|
-
type ToolCallEventSink,
|
|
12
|
-
} from "../toolCallEvents.js";
|
|
13
|
-
import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "../tools/index.js";
|
|
14
|
-
import { createApiTool } from "../tools/apiTool.js";
|
|
15
|
-
import type { AgentEventEmitter } from "../../agentEvents.js";
|
|
16
|
-
import type { SequenceDebugCollector } from "./sequenceDebug.js";
|
|
17
|
-
import { isAbortError } from "../../errors.js";
|
|
18
|
-
|
|
19
|
-
function getEnabledApiToolNames(messages: unknown[]) {
|
|
20
|
-
const enabledToolNames = new Set<string>();
|
|
21
|
-
|
|
22
|
-
for (const message of messages) {
|
|
23
|
-
if (!ToolMessage.isInstance(message) || message.name !== "fetch_tool_schema") {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const content =
|
|
28
|
-
typeof message.content === "string"
|
|
29
|
-
? message.content
|
|
30
|
-
: Array.isArray(message.content)
|
|
31
|
-
? message.content
|
|
32
|
-
.map((block) =>
|
|
33
|
-
typeof block === "string"
|
|
34
|
-
? block
|
|
35
|
-
: "text" in block
|
|
36
|
-
? block.text
|
|
37
|
-
: "",
|
|
38
|
-
)
|
|
39
|
-
.join("")
|
|
40
|
-
: "";
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const parsed = JSON.parse(content) as { status?: number; name?: string };
|
|
44
|
-
|
|
45
|
-
if (parsed.status === 200 && parsed.name) {
|
|
46
|
-
enabledToolNames.add(parsed.name);
|
|
47
|
-
}
|
|
48
|
-
} catch {}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return enabledToolNames;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function createApiBasedToolsMiddleware(
|
|
55
|
-
apiBasedTools: Record<string, ApiBasedTool>,
|
|
56
|
-
adminforth: IAdminForth,
|
|
57
|
-
) {
|
|
58
|
-
const alwaysAvailableApiToolNames = new Set<string>(ALWAYS_AVAILABLE_API_TOOL_NAMES);
|
|
59
|
-
const dynamicTools = Object.fromEntries(
|
|
60
|
-
Object.entries(apiBasedTools).map(([toolName, apiBasedTool]) => [
|
|
61
|
-
toolName,
|
|
62
|
-
createApiTool(toolName, apiBasedTool),
|
|
63
|
-
]),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
return createMiddleware({
|
|
67
|
-
name: "ApiBasedToolsMiddleware",
|
|
68
|
-
async wrapModelCall(request, handler) {
|
|
69
|
-
const enabledApiToolNames = getEnabledApiToolNames(request.state.messages);
|
|
70
|
-
const tools = [...enabledApiToolNames]
|
|
71
|
-
.filter((toolName) => !alwaysAvailableApiToolNames.has(toolName))
|
|
72
|
-
.map((toolName) => dynamicTools[toolName]);
|
|
73
|
-
const availableTools = [...request.tools, ...tools];
|
|
74
|
-
|
|
75
|
-
logger.info(
|
|
76
|
-
`AdminForth Agent callable tools: ${availableTools.map((tool) => tool.name).join(", ")}`,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
return handler({
|
|
80
|
-
...request,
|
|
81
|
-
tools: availableTools,
|
|
82
|
-
});
|
|
83
|
-
},
|
|
84
|
-
async wrapToolCall(request, handler) {
|
|
85
|
-
const startedAt = Date.now();
|
|
86
|
-
const toolInput = JSON.stringify(request.toolCall.args ?? {});
|
|
87
|
-
if (!request.toolCall.id) {
|
|
88
|
-
throw new Error(`Tool call "${request.toolCall.name}" has no id.`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const toolCallId = request.toolCall.id;
|
|
92
|
-
const { adminUser, abortSignal, emit, sequenceDebugSink, userTimeZone } = request.runtime.context as {
|
|
93
|
-
adminUser: AdminUser;
|
|
94
|
-
abortSignal?: AbortSignal;
|
|
95
|
-
emit?: AgentEventEmitter;
|
|
96
|
-
sequenceDebugSink: SequenceDebugCollector;
|
|
97
|
-
userTimeZone: string;
|
|
98
|
-
};
|
|
99
|
-
const emitToolCall: ToolCallEventSink = (event) => {
|
|
100
|
-
sequenceDebugSink.handleToolCallEvent(event);
|
|
101
|
-
void emit?.({
|
|
102
|
-
type: "tool-call",
|
|
103
|
-
data: event,
|
|
104
|
-
});
|
|
105
|
-
};
|
|
106
|
-
const toolArgs = (request.toolCall.args ?? {}) as Record<string, unknown>;
|
|
107
|
-
let toolInfo: string | undefined;
|
|
108
|
-
|
|
109
|
-
if (request.toolCall.name === "fetch_skill") {
|
|
110
|
-
toolInfo = `Load ${(toolArgs.skillName as string).split("_").join(" ")} skill`;
|
|
111
|
-
} else if (request.toolCall.name === "fetch_tool_schema") {
|
|
112
|
-
toolInfo = `Load ${(toolArgs.toolName as string).split("_").join(" ")} tool `;
|
|
113
|
-
} else {
|
|
114
|
-
toolInfo = await formatApiBasedToolCall({
|
|
115
|
-
adminforth,
|
|
116
|
-
adminUser,
|
|
117
|
-
inputs: toolArgs,
|
|
118
|
-
toolName: request.toolCall.name,
|
|
119
|
-
userTimeZone,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
const toolCallTracker = createToolCallTracker({
|
|
123
|
-
emit: emitToolCall,
|
|
124
|
-
toolCallId,
|
|
125
|
-
toolName: request.toolCall.name,
|
|
126
|
-
toolInfo,
|
|
127
|
-
input: toolArgs,
|
|
128
|
-
startedAt,
|
|
129
|
-
});
|
|
130
|
-
toolCallTracker.start();
|
|
131
|
-
logger.info(
|
|
132
|
-
`Invoking tool "${request.toolCall.name}" with input: ${toolInput}`,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
|
|
137
|
-
const result = getEnabledApiToolNames(request.state.messages).has(request.toolCall.name)
|
|
138
|
-
? await handler({
|
|
139
|
-
...request,
|
|
140
|
-
tool: dynamicTools[request.toolCall.name],
|
|
141
|
-
})
|
|
142
|
-
: await handler(request);
|
|
143
|
-
|
|
144
|
-
toolCallTracker.finishSuccess(result);
|
|
145
|
-
return result;
|
|
146
|
-
} catch (error) {
|
|
147
|
-
if (
|
|
148
|
-
isGraphInterrupt(error)
|
|
149
|
-
|| abortSignal?.aborted
|
|
150
|
-
|| isAbortError(error)
|
|
151
|
-
) {
|
|
152
|
-
throw error;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
156
|
-
|
|
157
|
-
logger.error(
|
|
158
|
-
`Error calling tool "${request.toolCall.name}": ${error instanceof Error ? error.stack ?? error.message : String(error)}`,
|
|
159
|
-
);
|
|
160
|
-
toolCallTracker.finishError(`Error: ${message}`);
|
|
161
|
-
return new ToolMessage({
|
|
162
|
-
name: request.toolCall.name,
|
|
163
|
-
tool_call_id: toolCallId,
|
|
164
|
-
status: "error",
|
|
165
|
-
content: `Error: ${message}`,
|
|
166
|
-
})
|
|
167
|
-
} finally {
|
|
168
|
-
logger.info(
|
|
169
|
-
`Tool "${request.toolCall.name}" finished in ${Date.now() - startedAt}ms`,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { AIMessage } from "@langchain/core/messages";
|
|
2
|
-
import { createMiddleware } from "langchain";
|
|
3
|
-
|
|
4
|
-
type OpenAiResponsesMetadata = {
|
|
5
|
-
id?: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
type OpenAiResponsesContext = {
|
|
9
|
-
sessionId: string;
|
|
10
|
-
turnId: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function getTurnKey(context: OpenAiResponsesContext) {
|
|
14
|
-
return `${context.sessionId}:${context.turnId}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getResponseId(message: AIMessage) {
|
|
18
|
-
const metadata = message.response_metadata as OpenAiResponsesMetadata | undefined;
|
|
19
|
-
return metadata?.id ?? null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getPreviousResponseId(modelSettings?: Record<string, unknown>) {
|
|
23
|
-
return (modelSettings as { previous_response_id?: string } | undefined)
|
|
24
|
-
?.previous_response_id;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getContinuationMessages<T extends { response_metadata?: unknown }>(
|
|
28
|
-
messages: T[],
|
|
29
|
-
previousResponseId: string,
|
|
30
|
-
) {
|
|
31
|
-
let continuationStartIndex: number | null = null;
|
|
32
|
-
|
|
33
|
-
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
34
|
-
const message = messages[index];
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
AIMessage.isInstance(message) &&
|
|
38
|
-
(message.response_metadata as OpenAiResponsesMetadata | undefined)?.id ===
|
|
39
|
-
previousResponseId
|
|
40
|
-
) {
|
|
41
|
-
continuationStartIndex = index + 1;
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (continuationStartIndex === null) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return messages.slice(continuationStartIndex);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function createOpenAiResponsesContinuationMiddleware() {
|
|
54
|
-
const responseIdsByTurn = new Map<string, string>();
|
|
55
|
-
|
|
56
|
-
return createMiddleware({
|
|
57
|
-
name: "OpenAiResponsesContinuationMiddleware",
|
|
58
|
-
async wrapModelCall(request, handler) {
|
|
59
|
-
const context = request.runtime.context as OpenAiResponsesContext;
|
|
60
|
-
const turnKey = getTurnKey(context);
|
|
61
|
-
const previousResponseId =
|
|
62
|
-
getPreviousResponseId(request.modelSettings) ??
|
|
63
|
-
responseIdsByTurn.get(turnKey);
|
|
64
|
-
const continuationMessages = previousResponseId
|
|
65
|
-
? getContinuationMessages(request.messages, previousResponseId)
|
|
66
|
-
: null;
|
|
67
|
-
|
|
68
|
-
const response = await handler(
|
|
69
|
-
previousResponseId && continuationMessages
|
|
70
|
-
? {
|
|
71
|
-
...request,
|
|
72
|
-
messages: continuationMessages,
|
|
73
|
-
modelSettings: {
|
|
74
|
-
...request.modelSettings,
|
|
75
|
-
previous_response_id: previousResponseId,
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
: request,
|
|
79
|
-
) as AIMessage;
|
|
80
|
-
|
|
81
|
-
const responseId = getResponseId(response);
|
|
82
|
-
|
|
83
|
-
if (responseId) {
|
|
84
|
-
responseIdsByTurn.set(turnKey, responseId);
|
|
85
|
-
} else {
|
|
86
|
-
responseIdsByTurn.delete(turnKey);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return response;
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
}
|