@relevanceai/sdk 3.0.2 → 3.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/LICENSE +1 -1
- package/README.md +49 -649
- package/esm/agent.js +4 -0
- package/esm/client.d.ts +1 -1
- package/esm/message/agent.d.ts +12 -0
- package/esm/message/agent.js +14 -0
- package/esm/message/stream.d.ts +15 -0
- package/esm/message/stream.js +11 -0
- package/esm/message/task.d.ts +12 -5
- package/esm/message/task.js +11 -3
- package/esm/region.d.ts +1 -0
- package/esm/region.js +3 -0
- package/esm/task/agent-strategy.js +12 -5
- package/esm/task/stream.d.ts +24 -0
- package/esm/task/stream.js +151 -0
- package/esm/task/task.d.ts +3 -1
- package/esm/task/task.js +50 -2
- package/esm/task/workforce-strategy.js +12 -5
- package/esm/workforce.js +1 -0
- package/package.json +1 -1
- package/script/agent.js +4 -0
- package/script/client.d.ts +1 -1
- package/script/message/agent.d.ts +12 -0
- package/script/message/agent.js +14 -0
- package/script/message/stream.d.ts +15 -0
- package/script/message/stream.js +16 -0
- package/script/message/task.d.ts +12 -5
- package/script/message/task.js +11 -3
- package/script/region.d.ts +1 -0
- package/script/region.js +4 -0
- package/script/task/agent-strategy.js +12 -5
- package/script/task/stream.d.ts +24 -0
- package/script/task/stream.js +155 -0
- package/script/task/task.d.ts +3 -1
- package/script/task/task.js +50 -2
- package/script/task/workforce-strategy.js +12 -5
- package/script/workforce.js +1 -0
package/esm/agent.js
CHANGED
|
@@ -201,6 +201,10 @@ export class Agent {
|
|
|
201
201
|
body: JSON.stringify({
|
|
202
202
|
agent_id: this.#config.agent_id,
|
|
203
203
|
conversation_id: taskId,
|
|
204
|
+
// @todo: embed keys cannot have streaming enabled due to override permissions
|
|
205
|
+
...(!this.client.isEmbedKey()
|
|
206
|
+
? { agent_override: { ...this.#config, use_streaming: true } }
|
|
207
|
+
: {}),
|
|
204
208
|
message: {
|
|
205
209
|
role: "user",
|
|
206
210
|
content: message,
|
package/esm/client.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare class Client {
|
|
|
30
30
|
get region(): Region;
|
|
31
31
|
get project(): string;
|
|
32
32
|
isEmbedKey(): boolean;
|
|
33
|
-
fetch<T>(input:
|
|
33
|
+
fetch<T>(input: string, init?: RequestInit): Promise<T>;
|
|
34
34
|
url(path: string): URL;
|
|
35
35
|
uploadTempFile(file: File): Promise<Attachment>;
|
|
36
36
|
}
|
package/esm/message/agent.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { GenericMessage, type TaskMessageData } from "./task.js";
|
|
|
3
3
|
export interface AgentMessageContent {
|
|
4
4
|
type: "agent-message";
|
|
5
5
|
text: string;
|
|
6
|
+
generating?: boolean;
|
|
7
|
+
thinking?: string[];
|
|
6
8
|
thought_about_tool_calls?: boolean;
|
|
7
9
|
}
|
|
8
10
|
export declare class AgentMessage extends GenericMessage<AgentMessageContent> {
|
|
@@ -10,5 +12,15 @@ export declare class AgentMessage extends GenericMessage<AgentMessageContent> {
|
|
|
10
12
|
constructor(message: TaskMessageData<AgentMessageContent>, agent?: Agent);
|
|
11
13
|
get text(): string;
|
|
12
14
|
get agentId(): string;
|
|
15
|
+
get thoughts(): string[];
|
|
16
|
+
isGenerating(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated
|
|
19
|
+
* Do not use this method as it is not a reliable indicator of whether the
|
|
20
|
+
* agent is thinking. It only indicates whether the message content includes
|
|
21
|
+
* a flag that suggests the agent is considering tool calls, but this may
|
|
22
|
+
* not always be present or accurate. This method will be removed in next
|
|
23
|
+
* major release.
|
|
24
|
+
*/
|
|
13
25
|
isThought(): boolean;
|
|
14
26
|
}
|
package/esm/message/agent.js
CHANGED
|
@@ -11,6 +11,20 @@ export class AgentMessage extends GenericMessage {
|
|
|
11
11
|
get agentId() {
|
|
12
12
|
return this.agent?.id ?? "";
|
|
13
13
|
}
|
|
14
|
+
get thoughts() {
|
|
15
|
+
return this.message.content.thinking ?? [];
|
|
16
|
+
}
|
|
17
|
+
isGenerating() {
|
|
18
|
+
return this.message.content.generating ?? false;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated
|
|
22
|
+
* Do not use this method as it is not a reliable indicator of whether the
|
|
23
|
+
* agent is thinking. It only indicates whether the message content includes
|
|
24
|
+
* a flag that suggests the agent is considering tool calls, but this may
|
|
25
|
+
* not always be present or accurate. This method will be removed in next
|
|
26
|
+
* major release.
|
|
27
|
+
*/
|
|
14
28
|
isThought() {
|
|
15
29
|
return this.message.content.thought_about_tool_calls ?? false;
|
|
16
30
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GenericMessage } from "./task.js";
|
|
2
|
+
export type ThinkingMessageContent = {
|
|
3
|
+
type: "agent-thinking";
|
|
4
|
+
text: string;
|
|
5
|
+
};
|
|
6
|
+
export type TypingMessageContent = {
|
|
7
|
+
type: "agent-typing";
|
|
8
|
+
text: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class ThinkingMessage extends GenericMessage<ThinkingMessageContent> {
|
|
11
|
+
get text(): string;
|
|
12
|
+
}
|
|
13
|
+
export declare class TypingMessage extends GenericMessage<TypingMessageContent> {
|
|
14
|
+
get text(): string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GenericMessage } from "./task.js";
|
|
2
|
+
export class ThinkingMessage extends GenericMessage {
|
|
3
|
+
get text() {
|
|
4
|
+
return this.message.content.text;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class TypingMessage extends GenericMessage {
|
|
8
|
+
get text() {
|
|
9
|
+
return this.message.content.text;
|
|
10
|
+
}
|
|
11
|
+
}
|
package/esm/message/task.d.ts
CHANGED
|
@@ -4,9 +4,11 @@ import type { ToolMessage } from "./tool.js";
|
|
|
4
4
|
import type { UserMessage } from "./user.js";
|
|
5
5
|
import type { WorkforceAgentMessage } from "./workforce-agent.js";
|
|
6
6
|
import type { WorkforceAgentHandoverMessage } from "./workforce-agent-handover.js";
|
|
7
|
-
|
|
7
|
+
import type { ThinkingMessage } from "./stream.js";
|
|
8
|
+
import type { TypingMessage } from "./stream.js";
|
|
9
|
+
export type AnyTaskMessage = AgentMessage | AgentErrorMessage | ThinkingMessage | TypingMessage | ToolMessage | UserMessage | WorkforceAgentMessage | WorkforceAgentHandoverMessage;
|
|
8
10
|
export type TaskMessageType = AnyTaskMessage["type"];
|
|
9
|
-
interface MessageContent {
|
|
11
|
+
export interface MessageContent {
|
|
10
12
|
type: AnyTaskMessage["type"];
|
|
11
13
|
}
|
|
12
14
|
export interface TaskMessageData<C extends MessageContent = MessageContent> {
|
|
@@ -54,10 +56,15 @@ export declare abstract class GenericMessage<C extends MessageContent = MessageC
|
|
|
54
56
|
*/
|
|
55
57
|
isUser(): this is UserMessage;
|
|
56
58
|
/**
|
|
57
|
-
* Returns if the message
|
|
59
|
+
* Returns if the message is agent thinking.
|
|
58
60
|
*
|
|
59
61
|
* @returns {boolean}
|
|
60
62
|
*/
|
|
61
|
-
|
|
63
|
+
isThinking(): this is ThinkingMessage;
|
|
64
|
+
/**
|
|
65
|
+
* Returns if the message is agent typing.
|
|
66
|
+
*
|
|
67
|
+
* @returns {boolean}
|
|
68
|
+
*/
|
|
69
|
+
isTyping(): this is TypingMessage;
|
|
62
70
|
}
|
|
63
|
-
export {};
|
package/esm/message/task.js
CHANGED
|
@@ -52,11 +52,19 @@ export class GenericMessage {
|
|
|
52
52
|
return this.type === "user-message";
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* Returns if the message
|
|
55
|
+
* Returns if the message is agent thinking.
|
|
56
56
|
*
|
|
57
57
|
* @returns {boolean}
|
|
58
58
|
*/
|
|
59
|
-
|
|
60
|
-
return this.type === "agent-
|
|
59
|
+
isThinking() {
|
|
60
|
+
return this.type === "agent-thinking";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns if the message is agent typing.
|
|
64
|
+
*
|
|
65
|
+
* @returns {boolean}
|
|
66
|
+
*/
|
|
67
|
+
isTyping() {
|
|
68
|
+
return this.type === "agent-typing";
|
|
61
69
|
}
|
|
62
70
|
}
|
package/esm/region.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export declare const REGION_US = "bcbe5a";
|
|
|
3
3
|
export declare const REGION_EU = "d7b62b";
|
|
4
4
|
export declare const REGION_AU = "f1db6c";
|
|
5
5
|
export declare function regionBaseURL(region: Region): string;
|
|
6
|
+
export declare function regionStreamingURL(region: Region, token: string): string;
|
package/esm/region.js
CHANGED
|
@@ -4,3 +4,6 @@ export const REGION_AU = "f1db6c";
|
|
|
4
4
|
export function regionBaseURL(region) {
|
|
5
5
|
return `https://api-${region}.stack.tryrelevance.com`;
|
|
6
6
|
}
|
|
7
|
+
export function regionStreamingURL(region, token) {
|
|
8
|
+
return `https://${region}.streaming.tryrelevance.com/v1/stream?authorization=Bearer+${token}`;
|
|
9
|
+
}
|
|
@@ -22,15 +22,22 @@ export class AgentStrategy {
|
|
|
22
22
|
return this.agent;
|
|
23
23
|
}
|
|
24
24
|
async getMetadata() {
|
|
25
|
-
const
|
|
25
|
+
const url = `/agents/${this.agent.id}/tasks/${this.id}/metadata?include_streaming_token=true`;
|
|
26
|
+
const res = await this.client.fetch(url);
|
|
26
27
|
return {
|
|
27
28
|
id: this.id,
|
|
28
29
|
region: this.client.region,
|
|
29
30
|
project: this.client.project,
|
|
30
|
-
name: metadata.conversation.title,
|
|
31
|
-
status: stateToStatus(metadata.conversation.state),
|
|
32
|
-
createdAt: new Date(metadata.insert_date),
|
|
33
|
-
updatedAt: new Date(metadata.update_date),
|
|
31
|
+
name: res.metadata.conversation.title,
|
|
32
|
+
status: stateToStatus(res.metadata.conversation.state),
|
|
33
|
+
createdAt: new Date(res.metadata.insert_date),
|
|
34
|
+
updatedAt: new Date(res.metadata.update_date),
|
|
35
|
+
streamingToken: res.streaming_token
|
|
36
|
+
? {
|
|
37
|
+
token: res.streaming_token.token,
|
|
38
|
+
expiresAt: res.streaming_token.expiry_time_ms,
|
|
39
|
+
}
|
|
40
|
+
: undefined,
|
|
34
41
|
};
|
|
35
42
|
}
|
|
36
43
|
async getMessages({ after = new Date(0) } = {}) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Emitter } from "../emitter.js";
|
|
2
|
+
import { type Region } from "../region.js";
|
|
3
|
+
import type { TaskStrategy } from "./task.js";
|
|
4
|
+
export type StreamingToken = {
|
|
5
|
+
token: string;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
};
|
|
8
|
+
export type StreamDetail = {
|
|
9
|
+
content: string;
|
|
10
|
+
documentId: string;
|
|
11
|
+
};
|
|
12
|
+
type TaskStreamEventMap = {
|
|
13
|
+
thinking: StreamDetail;
|
|
14
|
+
typing: StreamDetail;
|
|
15
|
+
};
|
|
16
|
+
export declare class TaskStream extends Emitter<TaskStreamEventMap> {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(strategy: TaskStrategy<any>, metadata: {
|
|
19
|
+
region: Region;
|
|
20
|
+
streamingToken: StreamingToken;
|
|
21
|
+
});
|
|
22
|
+
close(): void;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Emitter } from "../emitter.js";
|
|
2
|
+
import { regionStreamingURL } from "../region.js";
|
|
3
|
+
export class TaskStream extends Emitter {
|
|
4
|
+
#strategy;
|
|
5
|
+
#region;
|
|
6
|
+
#token;
|
|
7
|
+
#closed = false;
|
|
8
|
+
#refreshTimer = null;
|
|
9
|
+
#source = null;
|
|
10
|
+
#fetchController = null;
|
|
11
|
+
constructor(strategy, metadata) {
|
|
12
|
+
super();
|
|
13
|
+
this.#strategy = strategy;
|
|
14
|
+
this.#region = metadata.region;
|
|
15
|
+
this.#token = metadata.streamingToken;
|
|
16
|
+
this.#connect();
|
|
17
|
+
}
|
|
18
|
+
close() {
|
|
19
|
+
this.#closed = true;
|
|
20
|
+
this.#clearRefreshTimer();
|
|
21
|
+
this.#disconnect();
|
|
22
|
+
}
|
|
23
|
+
#connect() {
|
|
24
|
+
if (this.#closed)
|
|
25
|
+
return;
|
|
26
|
+
this.#scheduleRefresh();
|
|
27
|
+
if (typeof EventSource !== "undefined") {
|
|
28
|
+
this.#connectEventSource();
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
this.#connectFetch();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
#disconnect() {
|
|
35
|
+
this.#clearRefreshTimer();
|
|
36
|
+
if (this.#source) {
|
|
37
|
+
this.#source.close();
|
|
38
|
+
this.#source = null;
|
|
39
|
+
}
|
|
40
|
+
if (this.#fetchController) {
|
|
41
|
+
this.#fetchController.abort();
|
|
42
|
+
this.#fetchController = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
#connectEventSource() {
|
|
46
|
+
const url = regionStreamingURL(this.#region, this.#token.token);
|
|
47
|
+
const source = new EventSource(url);
|
|
48
|
+
this.#source = source;
|
|
49
|
+
source.addEventListener("message", (event) => {
|
|
50
|
+
this.#handleData(event.data);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
#connectFetch() {
|
|
54
|
+
const url = regionStreamingURL(this.#region, this.#token.token);
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
this.#fetchController = controller;
|
|
57
|
+
void (async () => {
|
|
58
|
+
try {
|
|
59
|
+
const response = await fetch(url, {
|
|
60
|
+
headers: { Accept: "text/event-stream" },
|
|
61
|
+
signal: controller.signal,
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok || !response.body)
|
|
64
|
+
return;
|
|
65
|
+
const reader = response.body.getReader();
|
|
66
|
+
const decoder = new TextDecoder();
|
|
67
|
+
let buffer = "";
|
|
68
|
+
while (true) {
|
|
69
|
+
const { done, value } = await reader.read();
|
|
70
|
+
if (done)
|
|
71
|
+
break;
|
|
72
|
+
buffer += decoder.decode(value, { stream: true });
|
|
73
|
+
const { events, remainder } = this.#parseSSE(buffer);
|
|
74
|
+
buffer = remainder;
|
|
75
|
+
for (const data of events) {
|
|
76
|
+
this.#handleData(data);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// aborted or network error — ignore if closed
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
84
|
+
}
|
|
85
|
+
#parseSSE(buffer) {
|
|
86
|
+
const events = [];
|
|
87
|
+
const blocks = buffer.split("\n\n");
|
|
88
|
+
const remainder = blocks.pop(); // last chunk may be incomplete
|
|
89
|
+
for (const block of blocks) {
|
|
90
|
+
for (const line of block.split("\n")) {
|
|
91
|
+
if (line.startsWith("data:")) {
|
|
92
|
+
events.push(line.slice(5).trimStart());
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { events, remainder };
|
|
97
|
+
}
|
|
98
|
+
#handleData(raw) {
|
|
99
|
+
try {
|
|
100
|
+
const data = JSON.parse(raw);
|
|
101
|
+
switch (data.type) {
|
|
102
|
+
case "thinking":
|
|
103
|
+
this.dispatchEvent(new CustomEvent("thinking", {
|
|
104
|
+
detail: { content: data.content, documentId: data.documentId },
|
|
105
|
+
}));
|
|
106
|
+
break;
|
|
107
|
+
case "text":
|
|
108
|
+
this.dispatchEvent(new CustomEvent("typing", {
|
|
109
|
+
detail: { content: data.content, documentId: data.documentId },
|
|
110
|
+
}));
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// ignore malformed JSON
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
#scheduleRefresh() {
|
|
119
|
+
this.#clearRefreshTimer();
|
|
120
|
+
const delay = this.#token.expiresAt - Date.now() - 30_000;
|
|
121
|
+
if (delay <= 0)
|
|
122
|
+
return; // already expired or about to
|
|
123
|
+
this.#refreshTimer = setTimeout(() => this.#refresh(), delay);
|
|
124
|
+
}
|
|
125
|
+
#clearRefreshTimer() {
|
|
126
|
+
if (this.#refreshTimer !== null) {
|
|
127
|
+
clearTimeout(this.#refreshTimer);
|
|
128
|
+
this.#refreshTimer = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async #refresh() {
|
|
132
|
+
if (this.#closed)
|
|
133
|
+
return;
|
|
134
|
+
try {
|
|
135
|
+
const metadata = await this.#strategy.getMetadata();
|
|
136
|
+
if (this.#closed)
|
|
137
|
+
return;
|
|
138
|
+
if (!metadata.streamingToken) {
|
|
139
|
+
this.close();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.#region = metadata.region;
|
|
143
|
+
this.#token = metadata.streamingToken;
|
|
144
|
+
this.#disconnect();
|
|
145
|
+
this.#connect();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// refresh failed — try again on next schedule
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
package/esm/task/task.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ import type { Region } from "../region.js";
|
|
|
4
4
|
import type { Workforce } from "../workforce.js";
|
|
5
5
|
import type { Agent } from "../agent.js";
|
|
6
6
|
import type { AgentErrorMessage } from "../message/agent-error.js";
|
|
7
|
+
import { type StreamingToken } from "./stream.js";
|
|
7
8
|
export type TaskStatus = "not-started" | "idle" | "paused" | "queued" | "running" | "action" | "completed" | "cancelled" | "error";
|
|
9
|
+
export type { StreamingToken };
|
|
8
10
|
export interface TaskMetadata {
|
|
9
11
|
id: string;
|
|
10
12
|
region: Region;
|
|
@@ -13,6 +15,7 @@ export interface TaskMetadata {
|
|
|
13
15
|
name: string;
|
|
14
16
|
createdAt: Date;
|
|
15
17
|
updatedAt: Date;
|
|
18
|
+
streamingToken?: StreamingToken;
|
|
16
19
|
}
|
|
17
20
|
type TaskEventMap = {
|
|
18
21
|
updated: undefined;
|
|
@@ -56,4 +59,3 @@ export declare class Task<S extends Agent | Workforce, E extends TaskEventMap =
|
|
|
56
59
|
handleEvent: (event: CustomEvent<E[K]>) => void;
|
|
57
60
|
} | null, options?: boolean | AddEventListenerOptions): void;
|
|
58
61
|
}
|
|
59
|
-
export {};
|
package/esm/task/task.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Emitter } from "../emitter.js";
|
|
2
2
|
import { abortPromise, delay } from "../utils.js";
|
|
3
3
|
import { TaskErrorEvent, TaskMessageEvent, TaskUpdateEvent } from "../event.js";
|
|
4
|
+
import { ThinkingMessage, TypingMessage } from "../message/stream.js";
|
|
5
|
+
import { TaskStream } from "./stream.js";
|
|
4
6
|
export const resetBackoffDuration = Symbol("resetBackoffDuration");
|
|
5
7
|
const backoffStartingDuration = 1_000;
|
|
6
8
|
const backoffMaxDuration = 60_000;
|
|
@@ -10,6 +12,7 @@ export class Task extends Emitter {
|
|
|
10
12
|
backoffDuration = 0;
|
|
11
13
|
strategy;
|
|
12
14
|
#metadata;
|
|
15
|
+
#stream = null;
|
|
13
16
|
constructor(metadata, strategy) {
|
|
14
17
|
super();
|
|
15
18
|
this.strategy = strategy;
|
|
@@ -51,6 +54,33 @@ export class Task extends Emitter {
|
|
|
51
54
|
return false;
|
|
52
55
|
}
|
|
53
56
|
}
|
|
57
|
+
#connectStream() {
|
|
58
|
+
const stream = new TaskStream(this.strategy, {
|
|
59
|
+
region: this.#metadata.region,
|
|
60
|
+
streamingToken: this.#metadata.streamingToken,
|
|
61
|
+
});
|
|
62
|
+
stream.addEventListener("thinking", (event) => {
|
|
63
|
+
this.dispatchEvent(new TaskMessageEvent(new ThinkingMessage({
|
|
64
|
+
item_id: event.detail.documentId,
|
|
65
|
+
insert_date_: new Date().toISOString(),
|
|
66
|
+
content: {
|
|
67
|
+
type: "agent-thinking",
|
|
68
|
+
text: event.detail.content,
|
|
69
|
+
},
|
|
70
|
+
})));
|
|
71
|
+
});
|
|
72
|
+
stream.addEventListener("typing", (event) => {
|
|
73
|
+
this.dispatchEvent(new TaskMessageEvent(new TypingMessage({
|
|
74
|
+
item_id: event.detail.documentId,
|
|
75
|
+
insert_date_: new Date().toISOString(),
|
|
76
|
+
content: {
|
|
77
|
+
type: "agent-typing",
|
|
78
|
+
text: event.detail.content,
|
|
79
|
+
},
|
|
80
|
+
})));
|
|
81
|
+
});
|
|
82
|
+
this.#stream = stream;
|
|
83
|
+
}
|
|
54
84
|
#subscribe() {
|
|
55
85
|
if (this.subscribed) {
|
|
56
86
|
return;
|
|
@@ -59,9 +89,12 @@ export class Task extends Emitter {
|
|
|
59
89
|
this.subscribed = subscribed; // subscribed ref
|
|
60
90
|
const isSubscribed = () => !subscribed.signal.aborted;
|
|
61
91
|
this.backoffDuration = backoffStartingDuration;
|
|
62
|
-
const cursor = new Date();
|
|
92
|
+
const cursor = new Date(this.#metadata.createdAt);
|
|
63
93
|
const emitted = new Set();
|
|
64
94
|
const pending = new Map();
|
|
95
|
+
if (this.#metadata.streamingToken) {
|
|
96
|
+
this.#connectStream();
|
|
97
|
+
}
|
|
65
98
|
void (async () => {
|
|
66
99
|
while (isSubscribed()) {
|
|
67
100
|
try {
|
|
@@ -78,6 +111,9 @@ export class Task extends Emitter {
|
|
|
78
111
|
hasChanges = true;
|
|
79
112
|
}
|
|
80
113
|
this.#metadata = metadata;
|
|
114
|
+
if (!this.#stream && metadata.streamingToken) {
|
|
115
|
+
this.#connectStream();
|
|
116
|
+
}
|
|
81
117
|
if (messages.length) {
|
|
82
118
|
for (const message of messages) {
|
|
83
119
|
if (emitted.has(message.id)) {
|
|
@@ -104,7 +140,17 @@ export class Task extends Emitter {
|
|
|
104
140
|
this.dispatchEvent(new TaskMessageEvent(message));
|
|
105
141
|
break;
|
|
106
142
|
}
|
|
107
|
-
case "agent-message":
|
|
143
|
+
case "agent-message": {
|
|
144
|
+
if (message.isAgent() && message.isGenerating()) {
|
|
145
|
+
pending.set(message.id, message);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
emitted.add(message.id);
|
|
149
|
+
pending.delete(message.id);
|
|
150
|
+
hasChanges = true;
|
|
151
|
+
this.dispatchEvent(new TaskMessageEvent(message));
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
108
154
|
case "user-message":
|
|
109
155
|
hasChanges = true;
|
|
110
156
|
emitted.add(message.id);
|
|
@@ -140,6 +186,8 @@ export class Task extends Emitter {
|
|
|
140
186
|
})();
|
|
141
187
|
}
|
|
142
188
|
unsubscribe() {
|
|
189
|
+
this.#stream?.close();
|
|
190
|
+
this.#stream = null;
|
|
143
191
|
this.subscribed?.abort();
|
|
144
192
|
this.subscribed = null;
|
|
145
193
|
this.backoff?.abort();
|
|
@@ -77,15 +77,22 @@ export class WorkforceStrategy {
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
async getMetadata() {
|
|
80
|
-
const
|
|
80
|
+
const url = `/workforce/tasks/${this.id}/metadata?include_streaming_token=true`;
|
|
81
|
+
const res = await this.client.fetch(url);
|
|
81
82
|
return {
|
|
82
83
|
id: this.id,
|
|
83
84
|
region: this.client.region,
|
|
84
85
|
project: this.client.project,
|
|
85
|
-
name: metadata.title,
|
|
86
|
-
status: WorkforceStrategy.convertStatus(metadata.state),
|
|
87
|
-
createdAt: new Date(metadata.insert_date),
|
|
88
|
-
updatedAt: new Date(metadata.update_date),
|
|
86
|
+
name: res.metadata.title,
|
|
87
|
+
status: WorkforceStrategy.convertStatus(res.metadata.state),
|
|
88
|
+
createdAt: new Date(res.metadata.insert_date),
|
|
89
|
+
updatedAt: new Date(res.metadata.update_date),
|
|
90
|
+
streamingToken: res.streaming_token
|
|
91
|
+
? {
|
|
92
|
+
token: res.streaming_token.token,
|
|
93
|
+
expiresAt: res.streaming_token.expiry_time_ms,
|
|
94
|
+
}
|
|
95
|
+
: undefined,
|
|
89
96
|
};
|
|
90
97
|
}
|
|
91
98
|
}
|
package/esm/workforce.js
CHANGED
package/package.json
CHANGED
package/script/agent.js
CHANGED
|
@@ -205,6 +205,10 @@ class Agent {
|
|
|
205
205
|
body: JSON.stringify({
|
|
206
206
|
agent_id: this.#config.agent_id,
|
|
207
207
|
conversation_id: taskId,
|
|
208
|
+
// @todo: embed keys cannot have streaming enabled due to override permissions
|
|
209
|
+
...(!this.client.isEmbedKey()
|
|
210
|
+
? { agent_override: { ...this.#config, use_streaming: true } }
|
|
211
|
+
: {}),
|
|
208
212
|
message: {
|
|
209
213
|
role: "user",
|
|
210
214
|
content: message,
|
package/script/client.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare class Client {
|
|
|
30
30
|
get region(): Region;
|
|
31
31
|
get project(): string;
|
|
32
32
|
isEmbedKey(): boolean;
|
|
33
|
-
fetch<T>(input:
|
|
33
|
+
fetch<T>(input: string, init?: RequestInit): Promise<T>;
|
|
34
34
|
url(path: string): URL;
|
|
35
35
|
uploadTempFile(file: File): Promise<Attachment>;
|
|
36
36
|
}
|
|
@@ -3,6 +3,8 @@ import { GenericMessage, type TaskMessageData } from "./task.js";
|
|
|
3
3
|
export interface AgentMessageContent {
|
|
4
4
|
type: "agent-message";
|
|
5
5
|
text: string;
|
|
6
|
+
generating?: boolean;
|
|
7
|
+
thinking?: string[];
|
|
6
8
|
thought_about_tool_calls?: boolean;
|
|
7
9
|
}
|
|
8
10
|
export declare class AgentMessage extends GenericMessage<AgentMessageContent> {
|
|
@@ -10,5 +12,15 @@ export declare class AgentMessage extends GenericMessage<AgentMessageContent> {
|
|
|
10
12
|
constructor(message: TaskMessageData<AgentMessageContent>, agent?: Agent);
|
|
11
13
|
get text(): string;
|
|
12
14
|
get agentId(): string;
|
|
15
|
+
get thoughts(): string[];
|
|
16
|
+
isGenerating(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated
|
|
19
|
+
* Do not use this method as it is not a reliable indicator of whether the
|
|
20
|
+
* agent is thinking. It only indicates whether the message content includes
|
|
21
|
+
* a flag that suggests the agent is considering tool calls, but this may
|
|
22
|
+
* not always be present or accurate. This method will be removed in next
|
|
23
|
+
* major release.
|
|
24
|
+
*/
|
|
13
25
|
isThought(): boolean;
|
|
14
26
|
}
|
package/script/message/agent.js
CHANGED
|
@@ -14,6 +14,20 @@ class AgentMessage extends task_js_1.GenericMessage {
|
|
|
14
14
|
get agentId() {
|
|
15
15
|
return this.agent?.id ?? "";
|
|
16
16
|
}
|
|
17
|
+
get thoughts() {
|
|
18
|
+
return this.message.content.thinking ?? [];
|
|
19
|
+
}
|
|
20
|
+
isGenerating() {
|
|
21
|
+
return this.message.content.generating ?? false;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @deprecated
|
|
25
|
+
* Do not use this method as it is not a reliable indicator of whether the
|
|
26
|
+
* agent is thinking. It only indicates whether the message content includes
|
|
27
|
+
* a flag that suggests the agent is considering tool calls, but this may
|
|
28
|
+
* not always be present or accurate. This method will be removed in next
|
|
29
|
+
* major release.
|
|
30
|
+
*/
|
|
17
31
|
isThought() {
|
|
18
32
|
return this.message.content.thought_about_tool_calls ?? false;
|
|
19
33
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GenericMessage } from "./task.js";
|
|
2
|
+
export type ThinkingMessageContent = {
|
|
3
|
+
type: "agent-thinking";
|
|
4
|
+
text: string;
|
|
5
|
+
};
|
|
6
|
+
export type TypingMessageContent = {
|
|
7
|
+
type: "agent-typing";
|
|
8
|
+
text: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class ThinkingMessage extends GenericMessage<ThinkingMessageContent> {
|
|
11
|
+
get text(): string;
|
|
12
|
+
}
|
|
13
|
+
export declare class TypingMessage extends GenericMessage<TypingMessageContent> {
|
|
14
|
+
get text(): string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypingMessage = exports.ThinkingMessage = void 0;
|
|
4
|
+
const task_js_1 = require("./task.js");
|
|
5
|
+
class ThinkingMessage extends task_js_1.GenericMessage {
|
|
6
|
+
get text() {
|
|
7
|
+
return this.message.content.text;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.ThinkingMessage = ThinkingMessage;
|
|
11
|
+
class TypingMessage extends task_js_1.GenericMessage {
|
|
12
|
+
get text() {
|
|
13
|
+
return this.message.content.text;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.TypingMessage = TypingMessage;
|