@framers/agentos 0.1.13 → 0.1.14
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 +120 -81
- package/dist/api/AgentOS.d.ts +27 -3
- package/dist/api/AgentOS.d.ts.map +1 -1
- package/dist/api/AgentOS.js +15 -0
- package/dist/api/AgentOS.js.map +1 -1
- package/dist/api/AgentOSOrchestrator.d.ts.map +1 -1
- package/dist/api/AgentOSOrchestrator.js +248 -106
- package/dist/api/AgentOSOrchestrator.js.map +1 -1
- package/dist/config/RetrievalAugmentorConfiguration.d.ts +1 -1
- package/dist/core/observability/index.d.ts +2 -0
- package/dist/core/observability/index.d.ts.map +1 -1
- package/dist/core/observability/index.js +1 -0
- package/dist/core/observability/index.js.map +1 -1
- package/dist/core/observability/otel.d.ts +101 -0
- package/dist/core/observability/otel.d.ts.map +1 -0
- package/dist/core/observability/otel.js +344 -0
- package/dist/core/observability/otel.js.map +1 -0
- package/dist/extensions/ExtensionManager.d.ts +92 -1
- package/dist/extensions/ExtensionManager.d.ts.map +1 -1
- package/dist/extensions/ExtensionManager.js +128 -47
- package/dist/extensions/ExtensionManager.js.map +1 -1
- package/dist/extensions/packs/schema-on-demand-pack.d.ts +34 -0
- package/dist/extensions/packs/schema-on-demand-pack.d.ts.map +1 -0
- package/dist/extensions/packs/schema-on-demand-pack.js +275 -0
- package/dist/extensions/packs/schema-on-demand-pack.js.map +1 -0
- package/dist/logging/PinoLogger.d.ts.map +1 -1
- package/dist/logging/PinoLogger.js +13 -4
- package/dist/logging/PinoLogger.js.map +1 -1
- package/dist/rag/IRetrievalAugmentor.d.ts +1 -1
- package/dist/rag/IRetrievalAugmentor.d.ts.map +1 -1
- package/dist/rag/IVectorStore.d.ts +18 -0
- package/dist/rag/IVectorStore.d.ts.map +1 -1
- package/dist/rag/RetrievalAugmentor.d.ts +2 -0
- package/dist/rag/RetrievalAugmentor.d.ts.map +1 -1
- package/dist/rag/RetrievalAugmentor.js +121 -11
- package/dist/rag/RetrievalAugmentor.js.map +1 -1
- package/dist/rag/graphrag/GraphRAGEngine.d.ts +1 -0
- package/dist/rag/graphrag/GraphRAGEngine.d.ts.map +1 -1
- package/dist/rag/graphrag/GraphRAGEngine.js +26 -2
- package/dist/rag/graphrag/GraphRAGEngine.js.map +1 -1
- package/dist/rag/graphrag/IGraphRAG.d.ts +7 -0
- package/dist/rag/graphrag/IGraphRAG.d.ts.map +1 -1
- package/dist/rag/implementations/vector_stores/SqlVectorStore.d.ts +3 -0
- package/dist/rag/implementations/vector_stores/SqlVectorStore.d.ts.map +1 -1
- package/dist/rag/implementations/vector_stores/SqlVectorStore.js +171 -39
- package/dist/rag/implementations/vector_stores/SqlVectorStore.js.map +1 -1
- package/dist/rag/reranking/index.d.ts +1 -1
- package/dist/rag/reranking/index.js +1 -1
- package/dist/rag/reranking/providers/CohereReranker.d.ts +3 -3
- package/dist/rag/reranking/providers/CohereReranker.d.ts.map +1 -1
- package/dist/rag/reranking/providers/CohereReranker.js +8 -3
- package/dist/rag/reranking/providers/CohereReranker.js.map +1 -1
- package/package.json +5 -1
|
@@ -18,6 +18,7 @@ import { normalizeUsage, snapshotPersonaDetails } from '../core/orchestration/he
|
|
|
18
18
|
import { DEFAULT_PROMPT_PROFILE_CONFIG, selectPromptProfile, } from '../core/prompting/PromptProfileRouter.js';
|
|
19
19
|
import { DEFAULT_ROLLING_SUMMARY_COMPACTION_CONFIG, maybeCompactConversationMessages, } from '../core/conversation/RollingSummaryCompactor.js';
|
|
20
20
|
import { DEFAULT_LONG_TERM_MEMORY_POLICY, hasAnyLongTermMemoryScope, LONG_TERM_MEMORY_POLICY_METADATA_KEY, resolveLongTermMemoryPolicy, } from '../core/conversation/LongTermMemoryPolicy.js';
|
|
21
|
+
import { getActiveTraceMetadata, recordAgentOSToolResultMetrics, recordAgentOSTurnMetrics, recordExceptionOnActiveSpan, runWithSpanContext, shouldIncludeTraceInAgentOSResponses, startAgentOSSpan, withAgentOSSpan, } from '../core/observability/otel.js';
|
|
21
22
|
function normalizeMode(value) {
|
|
22
23
|
return (value || '').trim().toLowerCase();
|
|
23
24
|
}
|
|
@@ -150,6 +151,16 @@ export class AgentOSOrchestrator {
|
|
|
150
151
|
if (!baseChunk.metadata.language)
|
|
151
152
|
baseChunk.metadata.language = ctx.languageNegotiation;
|
|
152
153
|
}
|
|
154
|
+
if (shouldIncludeTraceInAgentOSResponses() &&
|
|
155
|
+
(type === AgentOSResponseChunkType.METADATA_UPDATE ||
|
|
156
|
+
type === AgentOSResponseChunkType.FINAL_RESPONSE ||
|
|
157
|
+
type === AgentOSResponseChunkType.ERROR)) {
|
|
158
|
+
const traceMeta = getActiveTraceMetadata();
|
|
159
|
+
if (traceMeta) {
|
|
160
|
+
baseChunk.metadata = baseChunk.metadata || {};
|
|
161
|
+
baseChunk.metadata.trace = traceMeta;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
153
164
|
let chunk;
|
|
154
165
|
switch (type) {
|
|
155
166
|
case AgentOSResponseChunkType.TEXT_DELTA:
|
|
@@ -290,18 +301,46 @@ export class AgentOSOrchestrator {
|
|
|
290
301
|
this.ensureInitialized();
|
|
291
302
|
const agentOSStreamId = await this.dependencies.streamingManager.createStream();
|
|
292
303
|
console.log(`AgentOSOrchestrator: Starting turn for AgentOS Stream ${agentOSStreamId}, User ${input.userId}, Session ${input.sessionId}`);
|
|
304
|
+
const rootSpan = startAgentOSSpan('agentos.turn', {
|
|
305
|
+
attributes: {
|
|
306
|
+
'agentos.stream_id': agentOSStreamId,
|
|
307
|
+
'agentos.user_id': input.userId,
|
|
308
|
+
'agentos.session_id': input.sessionId,
|
|
309
|
+
'agentos.conversation_id': input.conversationId ?? '',
|
|
310
|
+
'agentos.persona_id': input.selectedPersonaId ?? '',
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
const run = async () => this._processTurnInternal(agentOSStreamId, input);
|
|
314
|
+
const promise = rootSpan ? runWithSpanContext(rootSpan, run) : run();
|
|
293
315
|
// Execute the turn processing asynchronously without awaiting it here,
|
|
294
316
|
// so this method can return the streamId quickly.
|
|
295
|
-
|
|
317
|
+
promise
|
|
318
|
+
.catch(async (criticalError) => {
|
|
319
|
+
if (rootSpan) {
|
|
320
|
+
try {
|
|
321
|
+
rootSpan.recordException(criticalError);
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// ignore
|
|
325
|
+
}
|
|
326
|
+
}
|
|
296
327
|
console.error(`AgentOSOrchestrator: Critical unhandled error in _processTurnInternal for stream ${agentOSStreamId}:`, criticalError);
|
|
297
328
|
try {
|
|
298
329
|
await this.pushErrorChunk(agentOSStreamId, input.selectedPersonaId || 'unknown_persona', 'orchestrator_critical', GMIErrorCode.INTERNAL_SERVER_ERROR, `A critical orchestration error occurred: ${criticalError.message}`, { name: criticalError.name, stack: criticalError.stack });
|
|
299
|
-
await this.dependencies.streamingManager.closeStream(agentOSStreamId,
|
|
330
|
+
await this.dependencies.streamingManager.closeStream(agentOSStreamId, 'Critical orchestrator error');
|
|
300
331
|
}
|
|
301
332
|
catch (cleanupError) {
|
|
302
333
|
console.error(`AgentOSOrchestrator: Error during critical error cleanup for stream ${agentOSStreamId}:`, cleanupError);
|
|
303
334
|
}
|
|
304
335
|
this.activeStreamContexts.delete(agentOSStreamId);
|
|
336
|
+
})
|
|
337
|
+
.finally(() => {
|
|
338
|
+
try {
|
|
339
|
+
rootSpan?.end();
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// ignore
|
|
343
|
+
}
|
|
305
344
|
});
|
|
306
345
|
return agentOSStreamId;
|
|
307
346
|
}
|
|
@@ -310,24 +349,38 @@ export class AgentOSOrchestrator {
|
|
|
310
349
|
* @private
|
|
311
350
|
*/
|
|
312
351
|
async _processTurnInternal(agentOSStreamId, input) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
352
|
+
const turnStartedAt = Date.now();
|
|
353
|
+
let turnMetricsStatus = 'ok';
|
|
354
|
+
let turnMetricsPersonaId = input.selectedPersonaId;
|
|
355
|
+
let turnMetricsUsage;
|
|
356
|
+
const selectedPersonaId = input.selectedPersonaId;
|
|
316
357
|
let gmi;
|
|
317
358
|
let conversationContext;
|
|
318
359
|
let currentPersonaId = input.selectedPersonaId;
|
|
319
360
|
let gmiInstanceIdForChunks = 'gmi_pending_init';
|
|
320
361
|
let organizationIdForMemory;
|
|
321
362
|
let longTermMemoryPolicy = null;
|
|
363
|
+
let didForceTerminate = false;
|
|
322
364
|
try {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
365
|
+
if (!selectedPersonaId) {
|
|
366
|
+
throw new GMIError('AgentOSOrchestrator requires a selectedPersonaId on AgentOSInput.', GMIErrorCode.VALIDATION_ERROR);
|
|
367
|
+
}
|
|
368
|
+
const gmiResult = await withAgentOSSpan('agentos.gmi.get_or_create', async (span) => {
|
|
369
|
+
span?.setAttribute('agentos.user_id', input.userId);
|
|
370
|
+
span?.setAttribute('agentos.session_id', input.sessionId);
|
|
371
|
+
span?.setAttribute('agentos.persona_id', selectedPersonaId);
|
|
372
|
+
if (typeof input.conversationId === 'string' && input.conversationId.trim()) {
|
|
373
|
+
span?.setAttribute('agentos.conversation_id', input.conversationId.trim());
|
|
374
|
+
}
|
|
375
|
+
return this.dependencies.gmiManager.getOrCreateGMIForSession(input.userId, input.sessionId, // This is AgentOS's session ID, GMI might have its own.
|
|
376
|
+
selectedPersonaId, input.conversationId, // Can be undefined, GMIManager might default to sessionId.
|
|
377
|
+
input.options?.preferredModelId, input.options?.preferredProviderId, input.userApiKeys);
|
|
378
|
+
});
|
|
327
379
|
gmi = gmiResult.gmi;
|
|
328
380
|
conversationContext = gmiResult.conversationContext;
|
|
329
381
|
currentPersonaId = gmi.getCurrentPrimaryPersonaId(); // Get actual personaId from GMI
|
|
330
382
|
gmiInstanceIdForChunks = gmi.getGMIId();
|
|
383
|
+
turnMetricsPersonaId = currentPersonaId;
|
|
331
384
|
const streamContext = {
|
|
332
385
|
gmi, userId: input.userId, sessionId: input.sessionId, personaId: currentPersonaId,
|
|
333
386
|
conversationId: conversationContext.sessionId, // Use actual conversation ID from context
|
|
@@ -369,6 +422,7 @@ export class AgentOSOrchestrator {
|
|
|
369
422
|
// Persist inbound user/system message to ConversationContext BEFORE any LLM call so persona switches
|
|
370
423
|
// and restarts preserve memory, even if the LLM fails.
|
|
371
424
|
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
425
|
+
const persistContext = conversationContext;
|
|
372
426
|
try {
|
|
373
427
|
if (gmiInput.type === GMIInteractionType.TEXT && typeof gmiInput.content === 'string') {
|
|
374
428
|
conversationContext.addMessage({
|
|
@@ -393,7 +447,11 @@ export class AgentOSOrchestrator {
|
|
|
393
447
|
metadata: { agentPersonaId: currentPersonaId, source: 'agentos_input_system' },
|
|
394
448
|
});
|
|
395
449
|
}
|
|
396
|
-
await
|
|
450
|
+
await withAgentOSSpan('agentos.conversation.save', async (span) => {
|
|
451
|
+
span?.setAttribute('agentos.stage', 'inbound');
|
|
452
|
+
span?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
453
|
+
await this.dependencies.conversationManager.saveConversation(persistContext);
|
|
454
|
+
});
|
|
397
455
|
}
|
|
398
456
|
catch (persistError) {
|
|
399
457
|
console.warn(`AgentOSOrchestrator: Failed to persist inbound message to ConversationContext for stream ${agentOSStreamId}.`, persistError);
|
|
@@ -615,8 +673,13 @@ export class AgentOSOrchestrator {
|
|
|
615
673
|
}
|
|
616
674
|
// Persist any compaction/router metadata updates prior to the main LLM call.
|
|
617
675
|
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
676
|
+
const persistContext = conversationContext;
|
|
618
677
|
try {
|
|
619
|
-
await
|
|
678
|
+
await withAgentOSSpan('agentos.conversation.save', async (span) => {
|
|
679
|
+
span?.setAttribute('agentos.stage', 'metadata');
|
|
680
|
+
span?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
681
|
+
await this.dependencies.conversationManager.saveConversation(persistContext);
|
|
682
|
+
});
|
|
620
683
|
}
|
|
621
684
|
catch (metadataPersistError) {
|
|
622
685
|
console.warn(`AgentOSOrchestrator: Failed to persist conversation metadata updates for stream ${agentOSStreamId}.`, metadataPersistError);
|
|
@@ -681,7 +744,6 @@ export class AgentOSOrchestrator {
|
|
|
681
744
|
let lastGMIOutput; // To store the result from handleToolResult or final processTurnStream result
|
|
682
745
|
while (continueProcessing && currentToolCallIteration < this.config.maxToolCallIterations) {
|
|
683
746
|
currentToolCallIteration++;
|
|
684
|
-
let gmiStreamIterator;
|
|
685
747
|
if (lastGMIOutput?.toolCalls && lastGMIOutput.toolCalls.length > 0) {
|
|
686
748
|
// This case should be handled by external call to orchestrateToolResult.
|
|
687
749
|
// If GMI's handleToolResult itself requests more tools *synchronously* in its GMIOutput,
|
|
@@ -708,33 +770,41 @@ export class AgentOSOrchestrator {
|
|
|
708
770
|
continueProcessing = false;
|
|
709
771
|
break;
|
|
710
772
|
}
|
|
711
|
-
|
|
712
|
-
|
|
773
|
+
if (!gmi) {
|
|
774
|
+
throw new Error('AgentOSOrchestrator: GMI not initialized (unexpected).');
|
|
713
775
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
776
|
+
const gmiForTurn = gmi;
|
|
777
|
+
await withAgentOSSpan('agentos.gmi.process_turn_stream', async (span) => {
|
|
778
|
+
span?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
779
|
+
span?.setAttribute('agentos.gmi_id', gmiInstanceIdForChunks);
|
|
780
|
+
span?.setAttribute('agentos.tool_call_iteration', currentToolCallIteration);
|
|
781
|
+
const gmiStreamIterator = gmiForTurn.processTurnStream(gmiInput); // For initial turn or if GMI internally continues
|
|
782
|
+
// Consume the async generator manually so we can capture its return value (GMIOutput).
|
|
783
|
+
// `for await...of` does not expose the generator return value, which caused placeholder
|
|
784
|
+
// FINAL_RESPONSE payloads (e.g. "Turn processing sequence complete.").
|
|
785
|
+
while (true) {
|
|
786
|
+
const { value, done } = await gmiStreamIterator.next();
|
|
787
|
+
if (done) {
|
|
788
|
+
lastGMIOutput = value;
|
|
789
|
+
continueProcessing = false;
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
792
|
+
const gmiChunk = value;
|
|
793
|
+
await this.transformAndPushGMIChunk(agentOSStreamId, streamContext, gmiChunk);
|
|
794
|
+
// NOTE: Tool calls may be executed internally by the GMI/tool orchestrator. Do not stop
|
|
795
|
+
// streaming on TOOL_CALL_REQUEST; treat it as informational for observers/UI.
|
|
796
|
+
if (gmiChunk.isFinal || gmiChunk.type === GMIOutputChunkType.FINAL_RESPONSE_MARKER) {
|
|
797
|
+
// Still keep consuming to capture the generator's return value.
|
|
798
|
+
continueProcessing = false;
|
|
799
|
+
}
|
|
731
800
|
}
|
|
732
|
-
}
|
|
801
|
+
});
|
|
733
802
|
if (!continueProcessing)
|
|
734
803
|
break; // Exit the while loop
|
|
735
804
|
} // End while
|
|
736
805
|
if (currentToolCallIteration >= this.config.maxToolCallIterations && continueProcessing) {
|
|
737
806
|
console.warn(`AgentOSOrchestrator: Max tool call iterations reached for stream ${agentOSStreamId}. Forcing termination.`);
|
|
807
|
+
didForceTerminate = true;
|
|
738
808
|
await this.pushErrorChunk(agentOSStreamId, currentPersonaId, gmiInstanceIdForChunks, GMIErrorCode.RATE_LIMIT_EXCEEDED, // Or a more specific code
|
|
739
809
|
'Agent reached maximum tool call iterations.', { maxIterations: this.config.maxToolCallIterations });
|
|
740
810
|
}
|
|
@@ -750,8 +820,21 @@ export class AgentOSOrchestrator {
|
|
|
750
820
|
isFinal: true,
|
|
751
821
|
responseText: gmi ? 'Processing complete.' : 'Processing ended.',
|
|
752
822
|
};
|
|
823
|
+
const normalizedUsage = normalizeUsage(finalGMIStateForResponse.usage);
|
|
824
|
+
if (normalizedUsage) {
|
|
825
|
+
turnMetricsUsage = {
|
|
826
|
+
totalTokens: normalizedUsage.totalTokens,
|
|
827
|
+
promptTokens: normalizedUsage.promptTokens,
|
|
828
|
+
completionTokens: normalizedUsage.completionTokens,
|
|
829
|
+
totalCostUSD: typeof normalizedUsage.totalCostUSD === 'number' ? normalizedUsage.totalCostUSD : undefined,
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
if (didForceTerminate || Boolean(finalGMIStateForResponse.error)) {
|
|
833
|
+
turnMetricsStatus = 'error';
|
|
834
|
+
}
|
|
753
835
|
// Persist assistant output into ConversationContext for durable memory / prompt reconstruction.
|
|
754
836
|
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
837
|
+
const persistContext = conversationContext;
|
|
755
838
|
try {
|
|
756
839
|
if (typeof finalGMIStateForResponse.responseText === 'string' && finalGMIStateForResponse.responseText.trim()) {
|
|
757
840
|
conversationContext.addMessage({
|
|
@@ -768,7 +851,11 @@ export class AgentOSOrchestrator {
|
|
|
768
851
|
metadata: { agentPersonaId: currentPersonaId, source: 'agentos_output_tool_calls' },
|
|
769
852
|
});
|
|
770
853
|
}
|
|
771
|
-
await
|
|
854
|
+
await withAgentOSSpan('agentos.conversation.save', async (span) => {
|
|
855
|
+
span?.setAttribute('agentos.stage', 'assistant_output');
|
|
856
|
+
span?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
857
|
+
await this.dependencies.conversationManager.saveConversation(persistContext);
|
|
858
|
+
});
|
|
772
859
|
}
|
|
773
860
|
catch (persistError) {
|
|
774
861
|
console.warn(`AgentOSOrchestrator: Failed to persist assistant output to ConversationContext for stream ${agentOSStreamId}.`, persistError);
|
|
@@ -783,7 +870,7 @@ export class AgentOSOrchestrator {
|
|
|
783
870
|
finalUiCommands: finalGMIStateForResponse.uiCommands,
|
|
784
871
|
audioOutput: finalGMIStateForResponse.audioOutput,
|
|
785
872
|
imageOutput: finalGMIStateForResponse.imageOutput,
|
|
786
|
-
usage:
|
|
873
|
+
usage: normalizedUsage,
|
|
787
874
|
reasoningTrace: finalGMIStateForResponse.reasoningTrace,
|
|
788
875
|
error: finalGMIStateForResponse.error,
|
|
789
876
|
updatedConversationContext: conversationContext ? conversationContext.toJSON() : undefined,
|
|
@@ -792,13 +879,21 @@ export class AgentOSOrchestrator {
|
|
|
792
879
|
await this.dependencies.streamingManager.closeStream(agentOSStreamId, "Processing complete.");
|
|
793
880
|
}
|
|
794
881
|
catch (error) {
|
|
882
|
+
turnMetricsStatus = 'error';
|
|
883
|
+
recordExceptionOnActiveSpan(error, `Error in orchestrateTurn for stream ${agentOSStreamId}`);
|
|
795
884
|
const gmiErr = GMIError.wrap?.(error, GMIErrorCode.GMI_PROCESSING_ERROR, `Error in orchestrateTurn for stream ${agentOSStreamId}`) ||
|
|
796
885
|
new GMIError(`Error in orchestrateTurn for stream ${agentOSStreamId}: ${error.message}`, GMIErrorCode.GMI_PROCESSING_ERROR, error);
|
|
797
886
|
console.error(`AgentOSOrchestrator: Error during _processTurnInternal for stream ${agentOSStreamId}:`, gmiErr);
|
|
798
|
-
await this.pushErrorChunk(agentOSStreamId, currentPersonaId, gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
|
|
887
|
+
await this.pushErrorChunk(agentOSStreamId, currentPersonaId ?? 'unknown_persona', gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
|
|
799
888
|
await this.dependencies.streamingManager.closeStream(agentOSStreamId, "Error during turn processing.");
|
|
800
889
|
}
|
|
801
890
|
finally {
|
|
891
|
+
recordAgentOSTurnMetrics({
|
|
892
|
+
durationMs: Date.now() - turnStartedAt,
|
|
893
|
+
status: turnMetricsStatus,
|
|
894
|
+
personaId: turnMetricsPersonaId,
|
|
895
|
+
usage: turnMetricsUsage,
|
|
896
|
+
});
|
|
802
897
|
// Stream is closed explicitly in the success/error paths; this finally block always
|
|
803
898
|
// clears internal state to avoid leaks.
|
|
804
899
|
this.activeStreamContexts.delete(agentOSStreamId);
|
|
@@ -823,6 +918,7 @@ export class AgentOSOrchestrator {
|
|
|
823
918
|
*/
|
|
824
919
|
async orchestrateToolResult(agentOSStreamId, toolCallId, toolName, toolOutput, isSuccess, errorMessage) {
|
|
825
920
|
this.ensureInitialized();
|
|
921
|
+
const startedAt = Date.now();
|
|
826
922
|
const streamContext = this.activeStreamContexts.get(agentOSStreamId);
|
|
827
923
|
if (!streamContext) {
|
|
828
924
|
const errMsg = `Orchestrator: Received tool result for unknown or inactive streamId: ${agentOSStreamId}. Tool: ${toolName}, CallID: ${toolCallId}`;
|
|
@@ -837,86 +933,128 @@ export class AgentOSOrchestrator {
|
|
|
837
933
|
: { type: 'error', error: { code: 'EXTERNAL_TOOL_ERROR', message: errorMessage || `External tool '${toolName}' execution failed.` } };
|
|
838
934
|
console.log(`AgentOSOrchestrator: Feeding tool result for stream ${agentOSStreamId}, GMI ${gmiInstanceIdForChunks}, tool call ${toolCallId} (${toolName}) back to GMI.`);
|
|
839
935
|
try {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
936
|
+
await withAgentOSSpan('agentos.tool_result', async (span) => {
|
|
937
|
+
span?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
938
|
+
span?.setAttribute('agentos.gmi_id', gmiInstanceIdForChunks);
|
|
939
|
+
span?.setAttribute('agentos.tool_call_id', toolCallId);
|
|
940
|
+
span?.setAttribute('agentos.tool_name', toolName);
|
|
941
|
+
span?.setAttribute('agentos.tool_success', isSuccess);
|
|
844
942
|
try {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
metadata: { agentPersonaId: personaId, source: 'agentos_tool_result', isSuccess },
|
|
851
|
-
});
|
|
852
|
-
await this.dependencies.conversationManager.saveConversation(conversationContext);
|
|
853
|
-
}
|
|
854
|
-
catch (persistError) {
|
|
855
|
-
console.warn(`AgentOSOrchestrator: Failed to persist tool result to ConversationContext for stream ${agentOSStreamId}.`, persistError);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
// GMI processes the tool result and gives a *final output for that step*
|
|
859
|
-
const gmiOutputAfterTool = await gmi.handleToolResult(toolCallId, toolName, toolResultPayload, userId, userApiKeys || {});
|
|
860
|
-
// Process the GMIOutput (which is not a stream of chunks)
|
|
861
|
-
await this.processGMIOutput(agentOSStreamId, streamContext, gmiOutputAfterTool, false);
|
|
862
|
-
// If GMIOutput indicates further tool calls are needed by the GMI
|
|
863
|
-
if (gmiOutputAfterTool.toolCalls && gmiOutputAfterTool.toolCalls.length > 0) {
|
|
864
|
-
await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TOOL_CALL_REQUEST, gmiInstanceIdForChunks, personaId, false, // Not final, more interaction expected
|
|
865
|
-
{ toolCalls: gmiOutputAfterTool.toolCalls, rationale: gmiOutputAfterTool.responseText || "Agent requires further tool execution." });
|
|
866
|
-
// The orchestrator now waits for another external call to `orchestrateToolResult` for these new calls.
|
|
867
|
-
}
|
|
868
|
-
else if (gmiOutputAfterTool.isFinal) {
|
|
869
|
-
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
870
|
-
try {
|
|
871
|
-
if (typeof gmiOutputAfterTool.responseText === 'string' && gmiOutputAfterTool.responseText.trim()) {
|
|
943
|
+
// Emit the tool result itself as a chunk
|
|
944
|
+
await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TOOL_RESULT_EMISSION, gmiInstanceIdForChunks, personaId, false, { toolCallId, toolName, toolResult: toolOutput, isSuccess, errorMessage });
|
|
945
|
+
// Persist tool result into ConversationContext for durable memory / prompt reconstruction.
|
|
946
|
+
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
947
|
+
try {
|
|
872
948
|
conversationContext.addMessage({
|
|
873
|
-
role: MessageRole.
|
|
874
|
-
content:
|
|
875
|
-
|
|
949
|
+
role: MessageRole.TOOL,
|
|
950
|
+
content: typeof toolOutput === 'string' ? toolOutput : JSON.stringify(toolOutput),
|
|
951
|
+
tool_call_id: toolCallId,
|
|
952
|
+
name: toolName,
|
|
953
|
+
metadata: { agentPersonaId: personaId, source: 'agentos_tool_result', isSuccess },
|
|
876
954
|
});
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
content: null,
|
|
882
|
-
tool_calls: gmiOutputAfterTool.toolCalls,
|
|
883
|
-
metadata: { agentPersonaId: personaId, source: 'agentos_output_tool_calls' },
|
|
955
|
+
await withAgentOSSpan('agentos.conversation.save', async (child) => {
|
|
956
|
+
child?.setAttribute('agentos.stage', 'tool_result');
|
|
957
|
+
child?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
958
|
+
await this.dependencies.conversationManager.saveConversation(conversationContext);
|
|
884
959
|
});
|
|
885
960
|
}
|
|
886
|
-
|
|
961
|
+
catch (persistError) {
|
|
962
|
+
console.warn(`AgentOSOrchestrator: Failed to persist tool result to ConversationContext for stream ${agentOSStreamId}.`, persistError);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
// GMI processes the tool result and gives a *final output for that step*
|
|
966
|
+
const gmiOutputAfterTool = await withAgentOSSpan('agentos.gmi.handle_tool_result', async (child) => {
|
|
967
|
+
child?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
968
|
+
child?.setAttribute('agentos.tool_call_id', toolCallId);
|
|
969
|
+
child?.setAttribute('agentos.tool_name', toolName);
|
|
970
|
+
child?.setAttribute('agentos.tool_success', isSuccess);
|
|
971
|
+
return gmi.handleToolResult(toolCallId, toolName, toolResultPayload, userId, userApiKeys || {});
|
|
972
|
+
});
|
|
973
|
+
// Process the GMIOutput (which is not a stream of chunks)
|
|
974
|
+
await this.processGMIOutput(agentOSStreamId, streamContext, gmiOutputAfterTool, false);
|
|
975
|
+
// If GMIOutput indicates further tool calls are needed by the GMI
|
|
976
|
+
if (gmiOutputAfterTool.toolCalls && gmiOutputAfterTool.toolCalls.length > 0) {
|
|
977
|
+
await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.TOOL_CALL_REQUEST, gmiInstanceIdForChunks, personaId, false, // Not final, more interaction expected
|
|
978
|
+
{
|
|
979
|
+
toolCalls: gmiOutputAfterTool.toolCalls,
|
|
980
|
+
rationale: gmiOutputAfterTool.responseText || 'Agent requires further tool execution.',
|
|
981
|
+
});
|
|
982
|
+
// The orchestrator now waits for another external call to `orchestrateToolResult` for these new calls.
|
|
887
983
|
}
|
|
888
|
-
|
|
889
|
-
|
|
984
|
+
else if (gmiOutputAfterTool.isFinal) {
|
|
985
|
+
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
986
|
+
try {
|
|
987
|
+
if (typeof gmiOutputAfterTool.responseText === 'string' &&
|
|
988
|
+
gmiOutputAfterTool.responseText.trim()) {
|
|
989
|
+
conversationContext.addMessage({
|
|
990
|
+
role: MessageRole.ASSISTANT,
|
|
991
|
+
content: gmiOutputAfterTool.responseText,
|
|
992
|
+
metadata: { agentPersonaId: personaId, source: 'agentos_output' },
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
else if (gmiOutputAfterTool.toolCalls && gmiOutputAfterTool.toolCalls.length > 0) {
|
|
996
|
+
conversationContext.addMessage({
|
|
997
|
+
role: MessageRole.ASSISTANT,
|
|
998
|
+
content: null,
|
|
999
|
+
tool_calls: gmiOutputAfterTool.toolCalls,
|
|
1000
|
+
metadata: { agentPersonaId: personaId, source: 'agentos_output_tool_calls' },
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
await withAgentOSSpan('agentos.conversation.save', async (child) => {
|
|
1004
|
+
child?.setAttribute('agentos.stage', 'assistant_output_after_tool');
|
|
1005
|
+
child?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
1006
|
+
await this.dependencies.conversationManager.saveConversation(conversationContext);
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
catch (persistError) {
|
|
1010
|
+
console.warn(`AgentOSOrchestrator: Failed to persist assistant output after tool result for stream ${agentOSStreamId}.`, persistError);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
// If it's final and no more tool calls, the interaction for this GMI processing cycle might be done.
|
|
1014
|
+
// Push a final response marker or the already pushed final data from processGMIOutput takes precedence.
|
|
1015
|
+
await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, personaId, true, {
|
|
1016
|
+
finalResponseText: gmiOutputAfterTool.responseText,
|
|
1017
|
+
finalToolCalls: gmiOutputAfterTool.toolCalls,
|
|
1018
|
+
finalUiCommands: gmiOutputAfterTool.uiCommands,
|
|
1019
|
+
audioOutput: gmiOutputAfterTool.audioOutput,
|
|
1020
|
+
imageOutput: gmiOutputAfterTool.imageOutput,
|
|
1021
|
+
usage: normalizeUsage(gmiOutputAfterTool.usage),
|
|
1022
|
+
reasoningTrace: gmiOutputAfterTool.reasoningTrace,
|
|
1023
|
+
error: gmiOutputAfterTool.error,
|
|
1024
|
+
updatedConversationContext: conversationContext.toJSON(),
|
|
1025
|
+
activePersonaDetails: snapshotPersonaDetails(gmi.getPersona?.()),
|
|
1026
|
+
});
|
|
1027
|
+
this.activeStreamContexts.delete(agentOSStreamId); // Clean up context for this completed flow
|
|
1028
|
+
await this.dependencies.streamingManager.closeStream(agentOSStreamId, 'Tool processing complete and final response generated.');
|
|
890
1029
|
}
|
|
1030
|
+
// If not final and no tool calls, the GMI might have provided intermediate text.
|
|
1031
|
+
// The stream remains open for further GMI internal processing or new user input.
|
|
891
1032
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
}
|
|
909
|
-
// If not final and no tool calls, the GMI might have provided intermediate text.
|
|
910
|
-
// The stream remains open for further GMI internal processing or new user input.
|
|
1033
|
+
catch (error) {
|
|
1034
|
+
const gmiErr = GMIError.wrap?.(error, GMIErrorCode.TOOL_ERROR, `Error in orchestrateToolResult for stream ${agentOSStreamId}`) ||
|
|
1035
|
+
new GMIError(`Error in orchestrateToolResult for stream ${agentOSStreamId}: ${error.message}`, GMIErrorCode.TOOL_ERROR, error);
|
|
1036
|
+
console.error(`AgentOSOrchestrator: Critical error processing tool result for stream ${agentOSStreamId}:`, gmiErr);
|
|
1037
|
+
await this.pushErrorChunk(agentOSStreamId, personaId, gmiInstanceIdForChunks, gmiErr.code, gmiErr.message, gmiErr.details);
|
|
1038
|
+
this.activeStreamContexts.delete(agentOSStreamId);
|
|
1039
|
+
await this.dependencies.streamingManager.closeStream(agentOSStreamId, 'Critical error during tool result processing.');
|
|
1040
|
+
throw gmiErr; // Re-throw to signal failure to caller if necessary
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
recordAgentOSToolResultMetrics({
|
|
1044
|
+
durationMs: Date.now() - startedAt,
|
|
1045
|
+
status: 'ok',
|
|
1046
|
+
toolName,
|
|
1047
|
+
toolSuccess: isSuccess,
|
|
1048
|
+
});
|
|
911
1049
|
}
|
|
912
1050
|
catch (error) {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
throw
|
|
1051
|
+
recordAgentOSToolResultMetrics({
|
|
1052
|
+
durationMs: Date.now() - startedAt,
|
|
1053
|
+
status: 'error',
|
|
1054
|
+
toolName,
|
|
1055
|
+
toolSuccess: isSuccess,
|
|
1056
|
+
});
|
|
1057
|
+
throw error;
|
|
920
1058
|
}
|
|
921
1059
|
}
|
|
922
1060
|
/**
|
|
@@ -948,7 +1086,11 @@ export class AgentOSOrchestrator {
|
|
|
948
1086
|
// to decide on looping or yielding ToolCallRequestChunks.
|
|
949
1087
|
if (gmiOutput.isFinal && (!gmiOutput.toolCalls || gmiOutput.toolCalls.length === 0)) {
|
|
950
1088
|
if (this.config.enableConversationalPersistence && conversationContext) {
|
|
951
|
-
await
|
|
1089
|
+
await withAgentOSSpan('agentos.conversation.save', async (span) => {
|
|
1090
|
+
span?.setAttribute('agentos.stage', 'gmi_output_final');
|
|
1091
|
+
span?.setAttribute('agentos.stream_id', agentOSStreamId);
|
|
1092
|
+
await this.dependencies.conversationManager.saveConversation(conversationContext);
|
|
1093
|
+
});
|
|
952
1094
|
}
|
|
953
1095
|
// This is a final response without further tool calls
|
|
954
1096
|
await this.pushChunkToStream(agentOSStreamId, AgentOSResponseChunkType.FINAL_RESPONSE, gmiInstanceIdForChunks, personaId, true, {
|