graphlit-client 1.0.20260626003 → 1.0.20260626005
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +180 -90
- package/dist/streaming/providers.js +6 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -8,10 +8,20 @@ import { attachPartialErrors } from "./partial-errors.js";
|
|
|
8
8
|
import * as Types from "./generated/graphql-types.js";
|
|
9
9
|
import * as Documents from "./generated/graphql-documents.js";
|
|
10
10
|
import { getServiceType, getModelName, getModelEnum, isAnthropicAdaptiveThinkingOnlyModel, isOpenAIResponsesEligibleModel, } from "./model-mapping.js";
|
|
11
|
+
class StreamingLoopPartialError extends Error {
|
|
12
|
+
originalError;
|
|
13
|
+
partialResult;
|
|
14
|
+
constructor(error, partialResult) {
|
|
15
|
+
super(error instanceof Error ? error.message : String(error));
|
|
16
|
+
this.name = "StreamingLoopPartialError";
|
|
17
|
+
this.originalError = error;
|
|
18
|
+
this.partialResult = partialResult;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
11
21
|
import { StuckDetector } from "./helpers/stuck-detector.js";
|
|
12
22
|
import { TurnEvaluator } from "./helpers/turn-evaluator.js";
|
|
13
23
|
import { TokenBudgetTracker, truncateToolResult, windowToolRounds, estimateTokens, DEFAULT_CONTEXT_STRATEGY, } from "./helpers/context-management.js";
|
|
14
|
-
import { assertRequiredToolChoiceConfig, deriveProviderToolChoicePolicy, normalizeToolVisibilityResult, toAnthropicToolChoice, toGoogleToolConfig, toOpenAIChatToolChoice, toOpenAIResponsesToolChoice, validateRequiredToolChoice, } from "./helpers/tool-visibility.js";
|
|
24
|
+
import { assertRequiredToolChoiceConfig, deriveProviderToolChoicePolicy, normalizeToolVisibilityResult, RequiredToolChoiceError, toAnthropicToolChoice, toGoogleToolConfig, toOpenAIChatToolChoice, toOpenAIResponsesToolChoice, validateRequiredToolChoice, } from "./helpers/tool-visibility.js";
|
|
15
25
|
import { isRetryableGraphQLTransportError, ProviderError, } from "./types/internal.js";
|
|
16
26
|
import { UIEventAdapter } from "./streaming/ui-event-adapter.js";
|
|
17
27
|
import { formatMessagesForOpenAI, formatMessagesForOpenAIResponsesInitialRound, extractInstructionsForOpenAIResponses, extractSystemInstructionParts, buildResponsesFunctionCallOutputItems, formatToolsForOpenAIResponses, formatMessagesForAnthropic, formatMessagesForGoogle, formatMessagesForMistral, formatMessagesForBedrock, } from "./streaming/llm-formatters.js";
|
|
@@ -133,6 +143,15 @@ function normalizeToolCallForExecution(toolCall) {
|
|
|
133
143
|
firstStatusAt: toolCall.firstStatusAt ?? undefined,
|
|
134
144
|
};
|
|
135
145
|
}
|
|
146
|
+
function cloneOpenAIResponsesInvocationState(state) {
|
|
147
|
+
if (!state)
|
|
148
|
+
return undefined;
|
|
149
|
+
return {
|
|
150
|
+
instructions: state.instructions,
|
|
151
|
+
initialInput: [...state.initialInput],
|
|
152
|
+
continuationItems: [...state.continuationItems],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
136
155
|
// Default eligible OpenAI GPT-5.4+ models to Responses. Explicit
|
|
137
156
|
// `useResponsesApi: false` still forces legacy Chat Completions.
|
|
138
157
|
const OPENAI_RESPONSES_AUTO_ROUTING_ENABLED = true;
|
|
@@ -6357,33 +6376,55 @@ class Graphlit {
|
|
|
6357
6376
|
DEFAULT_CONTEXT_STRATEGY.rebudgetThreshold,
|
|
6358
6377
|
};
|
|
6359
6378
|
// Run the streaming loop
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6379
|
+
let loopResult;
|
|
6380
|
+
let loopOriginalError;
|
|
6381
|
+
let loopErrorMessage;
|
|
6382
|
+
try {
|
|
6383
|
+
loopResult = await this.executeStreamingLoop({
|
|
6384
|
+
conversationId,
|
|
6385
|
+
specification,
|
|
6386
|
+
messages,
|
|
6387
|
+
tools,
|
|
6388
|
+
toolHandlers,
|
|
6389
|
+
uiAdapter,
|
|
6390
|
+
budgetTracker,
|
|
6391
|
+
contextStrategy: mergedStrategy,
|
|
6392
|
+
maxRounds,
|
|
6393
|
+
abortSignal,
|
|
6394
|
+
useResponsesApi,
|
|
6395
|
+
correlationId,
|
|
6396
|
+
persona,
|
|
6397
|
+
mimeType,
|
|
6398
|
+
data,
|
|
6399
|
+
additionalSystemInstructions,
|
|
6400
|
+
fallbackSpecifications,
|
|
6401
|
+
resolveTools,
|
|
6402
|
+
turnNumber: 0,
|
|
6403
|
+
initialToolCallCount: 0,
|
|
6404
|
+
initialPreviousToolCallNames: [],
|
|
6405
|
+
});
|
|
6406
|
+
}
|
|
6407
|
+
catch (loopError) {
|
|
6408
|
+
if (loopError instanceof StreamingLoopPartialError) {
|
|
6409
|
+
loopResult = loopError.partialResult;
|
|
6410
|
+
loopOriginalError = loopError.originalError;
|
|
6411
|
+
loopErrorMessage =
|
|
6412
|
+
loopError.originalError instanceof Error
|
|
6413
|
+
? loopError.originalError.message
|
|
6414
|
+
: String(loopError.originalError);
|
|
6415
|
+
}
|
|
6416
|
+
else {
|
|
6417
|
+
throw loopError;
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6383
6420
|
// Complete the conversation and get token count
|
|
6384
6421
|
let finalTokens;
|
|
6385
6422
|
const trimmedMessage = loopResult.finalAssistantMessage;
|
|
6386
|
-
|
|
6423
|
+
const shouldPersistTurn = trimmedMessage ||
|
|
6424
|
+
(loopErrorMessage && loopResult.intermediateMessages.length > 0);
|
|
6425
|
+
if (shouldPersistTurn) {
|
|
6426
|
+
const completionMessage = trimmedMessage ||
|
|
6427
|
+
`Agent run failed after partial tool execution: ${loopErrorMessage ?? "Unknown error"}`;
|
|
6387
6428
|
// Calculate metrics for completeConversation
|
|
6388
6429
|
const completionTime = uiAdapter.getCompletionTime();
|
|
6389
6430
|
const ttft = uiAdapter.getTTFT();
|
|
@@ -6402,11 +6443,13 @@ class Graphlit {
|
|
|
6402
6443
|
: undefined;
|
|
6403
6444
|
const finalInputMessage = finalMessageInputs?.[finalMessageInputs.length - 1];
|
|
6404
6445
|
const finalMessageAlreadyIncluded = finalInputMessage?.role === Types.ConversationRoleTypes.Assistant &&
|
|
6405
|
-
finalInputMessage.message?.trim() ===
|
|
6406
|
-
if (
|
|
6446
|
+
finalInputMessage.message?.trim() === completionMessage;
|
|
6447
|
+
if (trimmedMessage &&
|
|
6448
|
+
loopResult.lastRoundReasoning &&
|
|
6449
|
+
!finalMessageAlreadyIncluded) {
|
|
6407
6450
|
const completionInput = {
|
|
6408
6451
|
role: Types.ConversationRoleTypes.Assistant,
|
|
6409
|
-
message:
|
|
6452
|
+
message: completionMessage,
|
|
6410
6453
|
timestamp: new Date().toISOString(),
|
|
6411
6454
|
thinkingContent: loopResult.lastRoundReasoning.content,
|
|
6412
6455
|
};
|
|
@@ -6416,13 +6459,16 @@ class Graphlit {
|
|
|
6416
6459
|
}
|
|
6417
6460
|
finalMessageInputs = [...(finalMessageInputs || []), completionInput];
|
|
6418
6461
|
}
|
|
6419
|
-
const completeResponse = await this.completeConversation(
|
|
6462
|
+
const completeResponse = await this.completeConversation(completionMessage, conversationId, millisecondsToTimeSpan(completionTime), millisecondsToTimeSpan(ttft), throughput, collectedArtifacts.length > 0 ? collectedArtifacts : undefined, finalMessageInputs, correlationId);
|
|
6420
6463
|
finalTokens =
|
|
6421
6464
|
completeResponse.completeConversation?.message?.tokens ?? undefined;
|
|
6422
6465
|
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
6423
6466
|
console.log(`📊 [completeConversation] Tokens used: ${finalTokens || "unknown"}`);
|
|
6424
6467
|
}
|
|
6425
6468
|
}
|
|
6469
|
+
if (loopOriginalError) {
|
|
6470
|
+
throw loopOriginalError;
|
|
6471
|
+
}
|
|
6426
6472
|
if (loopResult.usage) {
|
|
6427
6473
|
uiAdapter.setUsageData(loopResult.usage);
|
|
6428
6474
|
}
|
|
@@ -6479,6 +6525,59 @@ class Graphlit {
|
|
|
6479
6525
|
let openAIResponsesState;
|
|
6480
6526
|
let openAIResponsesPendingToolMessages = [];
|
|
6481
6527
|
let visibleToolsForCurrentRound = tools;
|
|
6528
|
+
const buildIntermediateMessageInputs = () => persistedIntermediateMessages.map((msg) => {
|
|
6529
|
+
const input = {
|
|
6530
|
+
role: msg.role,
|
|
6531
|
+
message: msg.message,
|
|
6532
|
+
timestamp: msg.timestamp,
|
|
6533
|
+
};
|
|
6534
|
+
if (msg.toolCallId)
|
|
6535
|
+
input.toolCallId = msg.toolCallId;
|
|
6536
|
+
if (msg.toolCalls) {
|
|
6537
|
+
input.toolCalls = msg.toolCalls
|
|
6538
|
+
.filter((tc) => tc !== null)
|
|
6539
|
+
.map((tc) => ({
|
|
6540
|
+
id: tc.id,
|
|
6541
|
+
name: tc.name,
|
|
6542
|
+
arguments: tc.arguments,
|
|
6543
|
+
startedAt: tc.startedAt,
|
|
6544
|
+
completedAt: tc.completedAt,
|
|
6545
|
+
durationMs: tc.durationMs,
|
|
6546
|
+
status: tc.status,
|
|
6547
|
+
failedAt: tc.failedAt,
|
|
6548
|
+
firstStatusAt: tc.firstStatusAt,
|
|
6549
|
+
}));
|
|
6550
|
+
}
|
|
6551
|
+
if (msg.thinkingContent) {
|
|
6552
|
+
input.thinkingContent = msg.thinkingContent;
|
|
6553
|
+
if (msg.thinkingSignature) {
|
|
6554
|
+
input.thinkingSignature = msg.thinkingSignature;
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
return input;
|
|
6558
|
+
});
|
|
6559
|
+
const buildLoopResult = () => ({
|
|
6560
|
+
fullMessage,
|
|
6561
|
+
finalAssistantMessage,
|
|
6562
|
+
toolCallCount: totalToolCallCount,
|
|
6563
|
+
toolCallNames,
|
|
6564
|
+
errors,
|
|
6565
|
+
contextWindow: budgetTracker?.getUsageSnapshot(),
|
|
6566
|
+
contextActions,
|
|
6567
|
+
reasoning: lastRoundReasoning,
|
|
6568
|
+
intermediateMessages: buildIntermediateMessageInputs(),
|
|
6569
|
+
lastRoundReasoning,
|
|
6570
|
+
usedSpecification: specification,
|
|
6571
|
+
usage: (accumulatedUsage.rounds?.length ?? 0) > 0
|
|
6572
|
+
? accumulatedUsage
|
|
6573
|
+
: undefined,
|
|
6574
|
+
});
|
|
6575
|
+
const throwWithPartialResult = (error) => {
|
|
6576
|
+
if (persistedIntermediateMessages.length > 0) {
|
|
6577
|
+
throw new StreamingLoopPartialError(error, buildLoopResult());
|
|
6578
|
+
}
|
|
6579
|
+
throw error;
|
|
6580
|
+
};
|
|
6482
6581
|
const selectNextStreamingSpecification = (reason) => {
|
|
6483
6582
|
for (let candidateIndex = currentSpecificationIndex + 1; candidateIndex < streamingSpecifications.length; candidateIndex++) {
|
|
6484
6583
|
const candidate = streamingSpecifications[candidateIndex];
|
|
@@ -6588,6 +6687,7 @@ class Graphlit {
|
|
|
6588
6687
|
roundMessage = "";
|
|
6589
6688
|
roundReasoning = undefined;
|
|
6590
6689
|
roundUsage = undefined;
|
|
6690
|
+
const preAttemptOpenAIResponsesState = cloneOpenAIResponsesInvocationState(openAIResponsesState);
|
|
6591
6691
|
try {
|
|
6592
6692
|
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
6593
6693
|
console.log(`\n🔀 [Streaming Decision] Service: ${serviceType}, Round: ${currentRound}${providerAttempt > 0 ? `, Retry: ${providerAttempt}` : ""}`);
|
|
@@ -6611,10 +6711,7 @@ class Graphlit {
|
|
|
6611
6711
|
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING_MESSAGES) {
|
|
6612
6712
|
console.log(`🔍 [OpenAI Responses] Sending ${openAIResponsesState.initialInput.length} initial items and ${openAIResponsesState.continuationItems.length} continuation items`);
|
|
6613
6713
|
}
|
|
6614
|
-
const responsesResult = await this.streamWithOpenAIResponses(specification, messages, openAIResponsesPendingToolMessages, visibleToolsForCurrentRound, uiAdapter, abortSignal, openAIResponsesState, toOpenAIResponsesToolChoice(providerToolChoicePolicy)
|
|
6615
|
-
(currentRound === 0 && visibleToolsForCurrentRound?.length
|
|
6616
|
-
? "required"
|
|
6617
|
-
: undefined));
|
|
6714
|
+
const responsesResult = await this.streamWithOpenAIResponses(specification, messages, openAIResponsesPendingToolMessages, visibleToolsForCurrentRound, uiAdapter, abortSignal, openAIResponsesState, toOpenAIResponsesToolChoice(providerToolChoicePolicy));
|
|
6618
6715
|
roundMessage = responsesResult.message;
|
|
6619
6716
|
toolCalls = responsesResult.toolCalls;
|
|
6620
6717
|
openAIResponsesState = responsesResult.state;
|
|
@@ -6891,6 +6988,17 @@ class Graphlit {
|
|
|
6891
6988
|
catch (retryError) {
|
|
6892
6989
|
if (abortSignal?.aborted)
|
|
6893
6990
|
throw retryError;
|
|
6991
|
+
if (retryError instanceof RequiredToolChoiceError) {
|
|
6992
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
6993
|
+
console.log(`\n🔁 [Required Tool] ${retryError.message} Re-asking round ${currentRound} with the same visibility policy (attempt ${providerAttempt + 1}/${DEFAULT_PROVIDER_RETRIES + 1}).`);
|
|
6994
|
+
}
|
|
6995
|
+
uiAdapter.resetForRetry(preRoundMessage);
|
|
6996
|
+
openAIResponsesState = cloneOpenAIResponsesInvocationState(preAttemptOpenAIResponsesState);
|
|
6997
|
+
if (providerAttempt >= DEFAULT_PROVIDER_RETRIES) {
|
|
6998
|
+
throwWithPartialResult(retryError);
|
|
6999
|
+
}
|
|
7000
|
+
continue;
|
|
7001
|
+
}
|
|
6894
7002
|
const isRetryable = retryError instanceof ProviderError && retryError.retryable;
|
|
6895
7003
|
if (isRetryable &&
|
|
6896
7004
|
providerAttempt >= DEFAULT_PROVIDER_RETRIES &&
|
|
@@ -6900,17 +7008,19 @@ class Graphlit {
|
|
|
6900
7008
|
continue;
|
|
6901
7009
|
}
|
|
6902
7010
|
if (!isRetryable || providerAttempt >= DEFAULT_PROVIDER_RETRIES) {
|
|
6903
|
-
|
|
7011
|
+
throwWithPartialResult(retryError);
|
|
6904
7012
|
}
|
|
6905
7013
|
// Exponential backoff with jitter
|
|
6906
7014
|
const delay = Math.min(PROVIDER_RETRY_BASE_DELAY_MS * Math.pow(2, providerAttempt), PROVIDER_RETRY_MAX_DELAY_MS);
|
|
6907
7015
|
const jitter = Math.random() * delay * 0.1;
|
|
6908
7016
|
const totalDelay = Math.round(delay + jitter);
|
|
7017
|
+
const providerError = retryError;
|
|
6909
7018
|
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
6910
|
-
console.log(`\n🔄 [Retry] ${
|
|
7019
|
+
console.log(`\n🔄 [Retry] ${providerError.provider} error (attempt ${providerAttempt + 1}/${DEFAULT_PROVIDER_RETRIES}): ${providerError.message}. Retrying in ${totalDelay}ms...`);
|
|
6911
7020
|
}
|
|
6912
7021
|
// Reset adapter to pre-round state so partial tokens are cleared
|
|
6913
7022
|
uiAdapter.resetForRetry(preRoundMessage);
|
|
7023
|
+
openAIResponsesState = cloneOpenAIResponsesInvocationState(preAttemptOpenAIResponsesState);
|
|
6914
7024
|
await new Promise((resolve) => setTimeout(resolve, totalDelay));
|
|
6915
7025
|
}
|
|
6916
7026
|
} // end retry for-loop
|
|
@@ -7297,55 +7407,7 @@ class Graphlit {
|
|
|
7297
7407
|
}
|
|
7298
7408
|
currentRound++;
|
|
7299
7409
|
}
|
|
7300
|
-
|
|
7301
|
-
const intermediateMessages = persistedIntermediateMessages;
|
|
7302
|
-
const messageInputs = intermediateMessages.map((msg, idx) => {
|
|
7303
|
-
const input = {
|
|
7304
|
-
role: msg.role,
|
|
7305
|
-
message: msg.message,
|
|
7306
|
-
timestamp: msg.timestamp,
|
|
7307
|
-
};
|
|
7308
|
-
if (msg.toolCallId)
|
|
7309
|
-
input.toolCallId = msg.toolCallId;
|
|
7310
|
-
if (msg.toolCalls) {
|
|
7311
|
-
input.toolCalls = msg.toolCalls
|
|
7312
|
-
.filter((tc) => tc !== null)
|
|
7313
|
-
.map((tc) => ({
|
|
7314
|
-
id: tc.id,
|
|
7315
|
-
name: tc.name,
|
|
7316
|
-
arguments: tc.arguments,
|
|
7317
|
-
startedAt: tc.startedAt,
|
|
7318
|
-
completedAt: tc.completedAt,
|
|
7319
|
-
durationMs: tc.durationMs,
|
|
7320
|
-
status: tc.status,
|
|
7321
|
-
failedAt: tc.failedAt,
|
|
7322
|
-
firstStatusAt: tc.firstStatusAt,
|
|
7323
|
-
}));
|
|
7324
|
-
}
|
|
7325
|
-
if (msg.thinkingContent) {
|
|
7326
|
-
input.thinkingContent = msg.thinkingContent;
|
|
7327
|
-
if (msg.thinkingSignature) {
|
|
7328
|
-
input.thinkingSignature = msg.thinkingSignature;
|
|
7329
|
-
}
|
|
7330
|
-
}
|
|
7331
|
-
return input;
|
|
7332
|
-
});
|
|
7333
|
-
return {
|
|
7334
|
-
fullMessage,
|
|
7335
|
-
finalAssistantMessage,
|
|
7336
|
-
toolCallCount: totalToolCallCount,
|
|
7337
|
-
toolCallNames,
|
|
7338
|
-
errors,
|
|
7339
|
-
contextWindow: budgetTracker?.getUsageSnapshot(),
|
|
7340
|
-
contextActions,
|
|
7341
|
-
reasoning: lastRoundReasoning,
|
|
7342
|
-
intermediateMessages: messageInputs,
|
|
7343
|
-
lastRoundReasoning,
|
|
7344
|
-
usedSpecification: specification,
|
|
7345
|
-
usage: (accumulatedUsage.rounds?.length ?? 0) > 0
|
|
7346
|
-
? accumulatedUsage
|
|
7347
|
-
: undefined,
|
|
7348
|
-
};
|
|
7410
|
+
return buildLoopResult();
|
|
7349
7411
|
}
|
|
7350
7412
|
// ────────────────────────────────────────────────────────────────────────────
|
|
7351
7413
|
// runAgent — multi-turn agent harness
|
|
@@ -7770,6 +7832,7 @@ class Graphlit {
|
|
|
7770
7832
|
// 7. Execute streaming loop for this turn
|
|
7771
7833
|
const turnStart = Date.now();
|
|
7772
7834
|
let loopResult;
|
|
7835
|
+
let loopErrorMessage;
|
|
7773
7836
|
try {
|
|
7774
7837
|
loopResult = await this.executeStreamingLoop({
|
|
7775
7838
|
conversationId,
|
|
@@ -7793,6 +7856,18 @@ class Graphlit {
|
|
|
7793
7856
|
initialPreviousToolCallNames: turnResults.flatMap((t) => t.toolCalls),
|
|
7794
7857
|
});
|
|
7795
7858
|
}
|
|
7859
|
+
catch (loopError) {
|
|
7860
|
+
if (loopError instanceof StreamingLoopPartialError) {
|
|
7861
|
+
loopResult = loopError.partialResult;
|
|
7862
|
+
loopErrorMessage =
|
|
7863
|
+
loopError.originalError instanceof Error
|
|
7864
|
+
? loopError.originalError.message
|
|
7865
|
+
: String(loopError.originalError);
|
|
7866
|
+
}
|
|
7867
|
+
else {
|
|
7868
|
+
throw loopError;
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7796
7871
|
finally {
|
|
7797
7872
|
uiAdapter.dispose();
|
|
7798
7873
|
}
|
|
@@ -7805,8 +7880,12 @@ class Graphlit {
|
|
|
7805
7880
|
const taskCompleteThisTurn = this.detectTaskCompleteInMessages(loopResult.intermediateMessages);
|
|
7806
7881
|
const terminalTextThisTurn = this.detectTerminalTextCompletion(loopResult, options?.completionMode);
|
|
7807
7882
|
const trimmedMessage = loopResult.finalAssistantMessage;
|
|
7883
|
+
const shouldPersistTurn = trimmedMessage ||
|
|
7884
|
+
(loopErrorMessage && loopResult.intermediateMessages.length > 0);
|
|
7808
7885
|
// 8b. Complete conversation (persist turn)
|
|
7809
|
-
if (
|
|
7886
|
+
if (shouldPersistTurn) {
|
|
7887
|
+
const completionMessage = trimmedMessage ||
|
|
7888
|
+
`Agent run failed after partial tool execution: ${loopErrorMessage ?? "Unknown error"}`;
|
|
7810
7889
|
const completionTime = uiAdapter.getCompletionTime();
|
|
7811
7890
|
const ttft = uiAdapter.getTTFT();
|
|
7812
7891
|
const throughput = uiAdapter.getThroughput();
|
|
@@ -7830,11 +7909,13 @@ class Graphlit {
|
|
|
7830
7909
|
}
|
|
7831
7910
|
const finalInputMessage = turnMessageInputs?.[turnMessageInputs.length - 1];
|
|
7832
7911
|
const finalMessageAlreadyIncluded = finalInputMessage?.role === Types.ConversationRoleTypes.Assistant &&
|
|
7833
|
-
finalInputMessage.message?.trim() ===
|
|
7834
|
-
if (
|
|
7912
|
+
finalInputMessage.message?.trim() === completionMessage;
|
|
7913
|
+
if (trimmedMessage &&
|
|
7914
|
+
loopResult.lastRoundReasoning &&
|
|
7915
|
+
!finalMessageAlreadyIncluded) {
|
|
7835
7916
|
const completionInput = {
|
|
7836
7917
|
role: Types.ConversationRoleTypes.Assistant,
|
|
7837
|
-
message:
|
|
7918
|
+
message: completionMessage,
|
|
7838
7919
|
timestamp: new Date().toISOString(),
|
|
7839
7920
|
thinkingContent: loopResult.lastRoundReasoning.content,
|
|
7840
7921
|
};
|
|
@@ -7844,7 +7925,7 @@ class Graphlit {
|
|
|
7844
7925
|
}
|
|
7845
7926
|
turnMessageInputs = [...(turnMessageInputs || []), completionInput];
|
|
7846
7927
|
}
|
|
7847
|
-
await this.completeConversation(
|
|
7928
|
+
await this.completeConversation(completionMessage, conversationId, millisecondsToTimeSpan(completionTime), millisecondsToTimeSpan(ttft), throughput, undefined, turnMessageInputs, options?.correlationId);
|
|
7848
7929
|
// Emit completion event
|
|
7849
7930
|
uiAdapter.handleEvent({
|
|
7850
7931
|
type: "complete",
|
|
@@ -7854,6 +7935,10 @@ class Graphlit {
|
|
|
7854
7935
|
// 9. Build TurnResult
|
|
7855
7936
|
const turnDuration = Date.now() - turnStart;
|
|
7856
7937
|
const turnToolNames = [...new Set(loopResult.toolCallNames)].sort();
|
|
7938
|
+
const turnErrors = [
|
|
7939
|
+
...loopResult.errors,
|
|
7940
|
+
...(loopErrorMessage ? [loopErrorMessage] : []),
|
|
7941
|
+
];
|
|
7857
7942
|
const turnResult = {
|
|
7858
7943
|
turnNumber: turn,
|
|
7859
7944
|
prompt: currentPrompt,
|
|
@@ -7871,7 +7956,7 @@ class Graphlit {
|
|
|
7871
7956
|
contextActions: loopResult.contextActions.length > 0
|
|
7872
7957
|
? loopResult.contextActions
|
|
7873
7958
|
: undefined,
|
|
7874
|
-
errors:
|
|
7959
|
+
errors: turnErrors.length > 0 ? turnErrors : undefined,
|
|
7875
7960
|
usage: lastTurnUsage,
|
|
7876
7961
|
};
|
|
7877
7962
|
// Reset per-turn usage for next turn
|
|
@@ -7884,6 +7969,11 @@ class Graphlit {
|
|
|
7884
7969
|
// 10. Notify callback
|
|
7885
7970
|
options?.onTurnComplete?.(turnResult);
|
|
7886
7971
|
// 11. Evaluate turn
|
|
7972
|
+
if (loopErrorMessage) {
|
|
7973
|
+
status = "error";
|
|
7974
|
+
errorMessage = loopErrorMessage;
|
|
7975
|
+
break;
|
|
7976
|
+
}
|
|
7887
7977
|
// a. Terminal completion reached?
|
|
7888
7978
|
if (turnResult.completionReason) {
|
|
7889
7979
|
status = "completed";
|
|
@@ -545,7 +545,12 @@ onEvent, onComplete, abortSignal, thinkingConfig, toolChoice) {
|
|
|
545
545
|
...streamConfig.tools[streamConfig.tools.length - 1],
|
|
546
546
|
cache_control: { type: "ephemeral" },
|
|
547
547
|
};
|
|
548
|
-
if (toolChoice) {
|
|
548
|
+
if (toolChoice && thinkingConfig) {
|
|
549
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
550
|
+
console.log("🧠 [Anthropic] Skipping native tool_choice because thinking is enabled; required tool policy will be validated after the response.");
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
else if (toolChoice) {
|
|
549
554
|
streamConfig.tool_choice = toolChoice;
|
|
550
555
|
}
|
|
551
556
|
}
|