graphlit-client 1.0.20260217003 → 1.0.20260217004
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.d.ts +7 -0
- package/dist/client.js +134 -85
- package/dist/types/ui-events.d.ts +8 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ declare class Graphlit {
|
|
|
51
51
|
private bedrockClient?;
|
|
52
52
|
private deepseekClient?;
|
|
53
53
|
private xaiClient?;
|
|
54
|
+
private readonly conversationQueues;
|
|
54
55
|
constructor(organizationIdOrOptions?: string | GraphlitClientOptions, environmentId?: string, jwtSecret?: string, ownerId?: string, userId?: string, apiUri?: string);
|
|
55
56
|
/**
|
|
56
57
|
* Initialize the Apollo client without regenerating the token.
|
|
@@ -2525,6 +2526,12 @@ declare class Graphlit {
|
|
|
2525
2526
|
*/
|
|
2526
2527
|
promptAgent(prompt: string, conversationId?: string, specification?: Types.EntityReferenceInput, tools?: Types.ToolDefinitionInput[], toolHandlers?: Record<string, ToolHandler>, options?: AgentOptions, mimeType?: string, data?: string, // base64 encoded
|
|
2527
2528
|
contentFilter?: Types.ContentCriteriaInput, augmentedFilter?: Types.ContentCriteriaInput, correlationId?: string, persona?: Types.EntityReferenceInput): Promise<AgentResult>;
|
|
2529
|
+
/**
|
|
2530
|
+
* Serializes async work per conversation ID to prevent concurrent formatConversation /
|
|
2531
|
+
* completeConversation calls from racing each other. Each call chains after the previous
|
|
2532
|
+
* one for the same conversation, so messages are always processed in order.
|
|
2533
|
+
*/
|
|
2534
|
+
private enqueueForConversation;
|
|
2528
2535
|
/**
|
|
2529
2536
|
* Execute an agent with streaming response
|
|
2530
2537
|
* @param prompt - The user prompt
|
package/dist/client.js
CHANGED
|
@@ -155,6 +155,9 @@ class Graphlit {
|
|
|
155
155
|
bedrockClient;
|
|
156
156
|
deepseekClient;
|
|
157
157
|
xaiClient;
|
|
158
|
+
// Serializes streamAgent calls per conversation to prevent race conditions
|
|
159
|
+
// when a user sends a second message before the first response completes.
|
|
160
|
+
conversationQueues = new Map();
|
|
158
161
|
constructor(organizationIdOrOptions, environmentId, jwtSecret, ownerId, userId, apiUri) {
|
|
159
162
|
// Handle both old constructor signature and new options object
|
|
160
163
|
let options;
|
|
@@ -4686,6 +4689,29 @@ class Graphlit {
|
|
|
4686
4689
|
clearTimeout(timeoutId);
|
|
4687
4690
|
}
|
|
4688
4691
|
}
|
|
4692
|
+
/**
|
|
4693
|
+
* Serializes async work per conversation ID to prevent concurrent formatConversation /
|
|
4694
|
+
* completeConversation calls from racing each other. Each call chains after the previous
|
|
4695
|
+
* one for the same conversation, so messages are always processed in order.
|
|
4696
|
+
*/
|
|
4697
|
+
enqueueForConversation(conversationId, work, abortSignal) {
|
|
4698
|
+
const previous = this.conversationQueues.get(conversationId) ?? Promise.resolve();
|
|
4699
|
+
// Swallow errors from the previous call so a failed message doesn't
|
|
4700
|
+
// permanently block the queue for this conversation.
|
|
4701
|
+
// Check the abort signal before starting work so ESC while queued is instant.
|
|
4702
|
+
const next = previous.catch(() => { }).then(() => {
|
|
4703
|
+
if (abortSignal?.aborted)
|
|
4704
|
+
throw new Error("Operation aborted");
|
|
4705
|
+
return work();
|
|
4706
|
+
});
|
|
4707
|
+
this.conversationQueues.set(conversationId, next);
|
|
4708
|
+
next.finally(() => {
|
|
4709
|
+
if (this.conversationQueues.get(conversationId) === next) {
|
|
4710
|
+
this.conversationQueues.delete(conversationId);
|
|
4711
|
+
}
|
|
4712
|
+
});
|
|
4713
|
+
return next;
|
|
4714
|
+
}
|
|
4689
4715
|
/**
|
|
4690
4716
|
* Execute an agent with streaming response
|
|
4691
4717
|
* @param prompt - The user prompt
|
|
@@ -4744,107 +4770,130 @@ class Graphlit {
|
|
|
4744
4770
|
throw new Error("Failed to create conversation");
|
|
4745
4771
|
}
|
|
4746
4772
|
}
|
|
4747
|
-
//
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
}
|
|
4752
|
-
// Fallback to promptAgent using the same conversation and parameters
|
|
4753
|
-
const promptResult = await this.promptAgent(prompt, actualConversationId, // Preserve conversation
|
|
4754
|
-
specification, tools, toolHandlers, {
|
|
4755
|
-
maxToolRounds: maxRounds,
|
|
4756
|
-
}, mimeType, data, contentFilter, augmentedFilter, correlationId, persona);
|
|
4757
|
-
// Convert promptAgent result to streaming events
|
|
4773
|
+
// Serialize execution per-conversation: if the user sends a second message
|
|
4774
|
+
// before the first response completes, queue it so formatConversation /
|
|
4775
|
+
// completeConversation calls never interleave on the same conversation.
|
|
4776
|
+
if (this.conversationQueues.has(actualConversationId)) {
|
|
4758
4777
|
onEvent({
|
|
4759
|
-
type: "
|
|
4778
|
+
type: "conversation_queued",
|
|
4760
4779
|
conversationId: actualConversationId,
|
|
4761
4780
|
timestamp: new Date(),
|
|
4762
4781
|
});
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4782
|
+
}
|
|
4783
|
+
await this.enqueueForConversation(actualConversationId, async () => {
|
|
4784
|
+
// Check streaming support - fallback to promptAgent if not supported
|
|
4785
|
+
if (fullSpec && !this.supportsStreaming(fullSpec, tools)) {
|
|
4786
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
4787
|
+
console.log("\n⚠️ [streamAgent] Streaming not supported, falling back to promptAgent with same conversation");
|
|
4788
|
+
}
|
|
4789
|
+
// Fallback to promptAgent using the same conversation and parameters
|
|
4790
|
+
const promptResult = await this.promptAgent(prompt, actualConversationId, // Preserve conversation
|
|
4791
|
+
specification, tools, toolHandlers, {
|
|
4792
|
+
maxToolRounds: maxRounds,
|
|
4793
|
+
}, mimeType, data, contentFilter, augmentedFilter, correlationId, persona);
|
|
4794
|
+
// Convert promptAgent result to streaming events
|
|
4795
|
+
onEvent({
|
|
4796
|
+
type: "conversation_started",
|
|
4797
|
+
conversationId: actualConversationId,
|
|
4798
|
+
timestamp: new Date(),
|
|
4776
4799
|
});
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4800
|
+
// Debug logging for fallback
|
|
4801
|
+
if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
|
|
4802
|
+
console.log(`📊 [streamAgent fallback] promptAgent result:`, {
|
|
4803
|
+
hasMessage: !!promptResult.message,
|
|
4804
|
+
messageLength: promptResult.message?.length,
|
|
4805
|
+
toolCallsCount: promptResult.toolCalls?.length || 0,
|
|
4806
|
+
toolResultsCount: promptResult.toolResults?.length || 0,
|
|
4807
|
+
toolCalls: promptResult.toolCalls,
|
|
4808
|
+
toolResults: promptResult.toolResults?.map((tr) => ({
|
|
4809
|
+
name: tr.name,
|
|
4810
|
+
hasResult: !!tr.result,
|
|
4811
|
+
hasError: !!tr.error,
|
|
4812
|
+
})),
|
|
4785
4813
|
});
|
|
4786
4814
|
}
|
|
4815
|
+
// Emit tool events if there were tool calls
|
|
4816
|
+
if (promptResult.toolCalls && promptResult.toolCalls.length > 0) {
|
|
4817
|
+
for (const toolCall of promptResult.toolCalls) {
|
|
4818
|
+
onEvent({
|
|
4819
|
+
type: "tool_update",
|
|
4820
|
+
toolCall: toolCall,
|
|
4821
|
+
status: "completed",
|
|
4822
|
+
});
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
// Emit the final message as a single update (simulating streaming)
|
|
4826
|
+
onEvent({
|
|
4827
|
+
type: "message_update",
|
|
4828
|
+
message: {
|
|
4829
|
+
__typename: "ConversationMessage",
|
|
4830
|
+
message: promptResult.message,
|
|
4831
|
+
role: Types.ConversationRoleTypes.Assistant,
|
|
4832
|
+
timestamp: new Date().toISOString(),
|
|
4833
|
+
toolCalls: promptResult.toolCalls || [],
|
|
4834
|
+
},
|
|
4835
|
+
isStreaming: false,
|
|
4836
|
+
});
|
|
4837
|
+
// Emit completion event
|
|
4838
|
+
onEvent({
|
|
4839
|
+
type: "conversation_completed",
|
|
4840
|
+
message: {
|
|
4841
|
+
__typename: "ConversationMessage",
|
|
4842
|
+
message: promptResult.message,
|
|
4843
|
+
role: Types.ConversationRoleTypes.Assistant,
|
|
4844
|
+
timestamp: new Date().toISOString(),
|
|
4845
|
+
toolCalls: promptResult.toolCalls || [],
|
|
4846
|
+
},
|
|
4847
|
+
});
|
|
4848
|
+
return; // Exit early after successful fallback
|
|
4787
4849
|
}
|
|
4788
|
-
//
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
});
|
|
4800
|
-
// Emit completion event
|
|
4801
|
-
onEvent({
|
|
4802
|
-
type: "conversation_completed",
|
|
4803
|
-
message: {
|
|
4804
|
-
__typename: "ConversationMessage",
|
|
4805
|
-
message: promptResult.message,
|
|
4806
|
-
role: Types.ConversationRoleTypes.Assistant,
|
|
4807
|
-
timestamp: new Date().toISOString(),
|
|
4808
|
-
toolCalls: promptResult.toolCalls || [],
|
|
4809
|
-
},
|
|
4850
|
+
// Create UI event adapter with model information
|
|
4851
|
+
const modelName = fullSpec ? getModelName(fullSpec) : undefined;
|
|
4852
|
+
const modelEnum = fullSpec ? getModelEnum(fullSpec) : undefined;
|
|
4853
|
+
const serviceType = fullSpec ? getServiceType(fullSpec) : undefined;
|
|
4854
|
+
uiAdapter = new UIEventAdapter(onEvent, actualConversationId, {
|
|
4855
|
+
smoothingEnabled: options?.smoothingEnabled ?? true,
|
|
4856
|
+
chunkingStrategy: options?.chunkingStrategy ?? "word",
|
|
4857
|
+
smoothingDelay: options?.smoothingDelay ?? 30,
|
|
4858
|
+
model: modelEnum,
|
|
4859
|
+
modelName: modelName,
|
|
4860
|
+
modelService: serviceType,
|
|
4810
4861
|
});
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
const modelName = fullSpec ? getModelName(fullSpec) : undefined;
|
|
4815
|
-
const modelEnum = fullSpec ? getModelEnum(fullSpec) : undefined;
|
|
4816
|
-
const serviceType = fullSpec ? getServiceType(fullSpec) : undefined;
|
|
4817
|
-
uiAdapter = new UIEventAdapter(onEvent, actualConversationId, {
|
|
4818
|
-
smoothingEnabled: options?.smoothingEnabled ?? true,
|
|
4819
|
-
chunkingStrategy: options?.chunkingStrategy ?? "word",
|
|
4820
|
-
smoothingDelay: options?.smoothingDelay ?? 30,
|
|
4821
|
-
model: modelEnum,
|
|
4822
|
-
modelName: modelName,
|
|
4823
|
-
modelService: serviceType,
|
|
4824
|
-
});
|
|
4825
|
-
// Start the streaming conversation
|
|
4826
|
-
await this.executeStreamingAgent(prompt, actualConversationId, fullSpec, tools, toolHandlers, uiAdapter, maxRounds, abortSignal, mimeType, data, correlationId, persona);
|
|
4862
|
+
// Start the streaming conversation
|
|
4863
|
+
await this.executeStreamingAgent(prompt, actualConversationId, fullSpec, tools, toolHandlers, uiAdapter, maxRounds, abortSignal, mimeType, data, correlationId, persona);
|
|
4864
|
+
}, abortSignal);
|
|
4827
4865
|
}
|
|
4828
4866
|
catch (error) {
|
|
4829
|
-
const
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
type: "error",
|
|
4833
|
-
error: errorMessage,
|
|
4834
|
-
});
|
|
4835
|
-
}
|
|
4836
|
-
else {
|
|
4837
|
-
// Fallback error event
|
|
4867
|
+
const isAbortError = (error instanceof Error && error.message === "Operation aborted") ||
|
|
4868
|
+
(error instanceof DOMException && error.name === "AbortError");
|
|
4869
|
+
if (isAbortError) {
|
|
4838
4870
|
onEvent({
|
|
4839
|
-
type: "
|
|
4840
|
-
error: {
|
|
4841
|
-
message: errorMessage,
|
|
4842
|
-
recoverable: false,
|
|
4843
|
-
},
|
|
4871
|
+
type: "conversation_cancelled",
|
|
4844
4872
|
conversationId: conversationId || "",
|
|
4845
4873
|
timestamp: new Date(),
|
|
4846
4874
|
});
|
|
4847
4875
|
}
|
|
4876
|
+
else {
|
|
4877
|
+
const errorMessage = error instanceof Error ? error.message : "Streaming failed";
|
|
4878
|
+
if (uiAdapter) {
|
|
4879
|
+
uiAdapter.handleEvent({
|
|
4880
|
+
type: "error",
|
|
4881
|
+
error: errorMessage,
|
|
4882
|
+
});
|
|
4883
|
+
}
|
|
4884
|
+
else {
|
|
4885
|
+
// Fallback error event
|
|
4886
|
+
onEvent({
|
|
4887
|
+
type: "error",
|
|
4888
|
+
error: {
|
|
4889
|
+
message: errorMessage,
|
|
4890
|
+
recoverable: false,
|
|
4891
|
+
},
|
|
4892
|
+
conversationId: conversationId || "",
|
|
4893
|
+
timestamp: new Date(),
|
|
4894
|
+
});
|
|
4895
|
+
}
|
|
4896
|
+
}
|
|
4848
4897
|
throw error;
|
|
4849
4898
|
}
|
|
4850
4899
|
finally {
|
|
@@ -35,6 +35,14 @@ export type AgentStreamEvent = {
|
|
|
35
35
|
conversationId: string;
|
|
36
36
|
timestamp: Date;
|
|
37
37
|
model?: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: "conversation_queued";
|
|
40
|
+
conversationId: string;
|
|
41
|
+
timestamp: Date;
|
|
42
|
+
} | {
|
|
43
|
+
type: "conversation_cancelled";
|
|
44
|
+
conversationId: string;
|
|
45
|
+
timestamp: Date;
|
|
38
46
|
} | ContextWindowEvent | {
|
|
39
47
|
type: "message_update";
|
|
40
48
|
message: StreamingConversationMessage;
|