@copilotkitnext/agent 1.51.4 → 1.51.5-next.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/CHANGELOG.md +6 -0
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +51 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/basic-agent.test.ts +427 -0
- package/src/__tests__/test-helpers.ts +32 -0
- package/src/index.ts +75 -4
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
toolCallDelta,
|
|
14
14
|
toolCall,
|
|
15
15
|
toolResult,
|
|
16
|
+
reasoningStart,
|
|
17
|
+
reasoningDelta,
|
|
18
|
+
reasoningEnd,
|
|
16
19
|
} from "./test-helpers";
|
|
17
20
|
|
|
18
21
|
// Mock the ai module
|
|
@@ -818,4 +821,428 @@ describe("BasicAgent", () => {
|
|
|
818
821
|
}
|
|
819
822
|
});
|
|
820
823
|
});
|
|
824
|
+
|
|
825
|
+
describe("Reasoning Event Emission", () => {
|
|
826
|
+
it("should emit full reasoning lifecycle events", async () => {
|
|
827
|
+
const agent = new BasicAgent({
|
|
828
|
+
model: "openai/gpt-4o",
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
vi.mocked(streamText).mockReturnValue(
|
|
832
|
+
mockStreamTextResponse([
|
|
833
|
+
reasoningStart(),
|
|
834
|
+
reasoningDelta("Let me think..."),
|
|
835
|
+
reasoningDelta(" about this."),
|
|
836
|
+
reasoningEnd(),
|
|
837
|
+
finish(),
|
|
838
|
+
]) as any,
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
const input: RunAgentInput = {
|
|
842
|
+
threadId: "thread1",
|
|
843
|
+
runId: "run1",
|
|
844
|
+
messages: [],
|
|
845
|
+
tools: [],
|
|
846
|
+
context: [],
|
|
847
|
+
state: {},
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const events = await collectEvents(agent["run"](input));
|
|
851
|
+
|
|
852
|
+
// Verify event order
|
|
853
|
+
const eventTypes = events.map((e: any) => e.type);
|
|
854
|
+
expect(eventTypes[0]).toBe(EventType.RUN_STARTED);
|
|
855
|
+
|
|
856
|
+
const reasoningStartIdx = eventTypes.indexOf(EventType.REASONING_START);
|
|
857
|
+
const reasoningMsgStartIdx = eventTypes.indexOf(
|
|
858
|
+
EventType.REASONING_MESSAGE_START,
|
|
859
|
+
);
|
|
860
|
+
const reasoningContentIndices = eventTypes.reduce(
|
|
861
|
+
(acc: number[], type: string, idx: number) =>
|
|
862
|
+
type === EventType.REASONING_MESSAGE_CONTENT ? [...acc, idx] : acc,
|
|
863
|
+
[],
|
|
864
|
+
);
|
|
865
|
+
const reasoningMsgEndIdx = eventTypes.indexOf(
|
|
866
|
+
EventType.REASONING_MESSAGE_END,
|
|
867
|
+
);
|
|
868
|
+
const reasoningEndIdx = eventTypes.indexOf(EventType.REASONING_END);
|
|
869
|
+
|
|
870
|
+
expect(reasoningStartIdx).toBeGreaterThan(0);
|
|
871
|
+
expect(reasoningMsgStartIdx).toBeGreaterThan(reasoningStartIdx);
|
|
872
|
+
expect(reasoningContentIndices).toHaveLength(2);
|
|
873
|
+
expect(reasoningContentIndices[0]).toBeGreaterThan(reasoningMsgStartIdx);
|
|
874
|
+
expect(reasoningMsgEndIdx).toBeGreaterThan(
|
|
875
|
+
reasoningContentIndices[reasoningContentIndices.length - 1],
|
|
876
|
+
);
|
|
877
|
+
expect(reasoningEndIdx).toBeGreaterThan(reasoningMsgEndIdx);
|
|
878
|
+
|
|
879
|
+
// Verify consistent messageId across all reasoning events
|
|
880
|
+
const reasoningEvents = events.filter((e: any) =>
|
|
881
|
+
[
|
|
882
|
+
EventType.REASONING_START,
|
|
883
|
+
EventType.REASONING_MESSAGE_START,
|
|
884
|
+
EventType.REASONING_MESSAGE_CONTENT,
|
|
885
|
+
EventType.REASONING_MESSAGE_END,
|
|
886
|
+
EventType.REASONING_END,
|
|
887
|
+
].includes(e.type),
|
|
888
|
+
);
|
|
889
|
+
const messageIds = reasoningEvents.map((e: any) => e.messageId);
|
|
890
|
+
expect(new Set(messageIds).size).toBe(1);
|
|
891
|
+
|
|
892
|
+
// Verify REASONING_MESSAGE_START has role "reasoning"
|
|
893
|
+
const msgStartEvent = events.find(
|
|
894
|
+
(e: any) => e.type === EventType.REASONING_MESSAGE_START,
|
|
895
|
+
);
|
|
896
|
+
expect(msgStartEvent).toMatchObject({ role: "reasoning" });
|
|
897
|
+
|
|
898
|
+
// Verify content deltas
|
|
899
|
+
const contentEvents = events.filter(
|
|
900
|
+
(e: any) => e.type === EventType.REASONING_MESSAGE_CONTENT,
|
|
901
|
+
);
|
|
902
|
+
expect(contentEvents[0]).toMatchObject({ delta: "Let me think..." });
|
|
903
|
+
expect(contentEvents[1]).toMatchObject({ delta: " about this." });
|
|
904
|
+
|
|
905
|
+
// Verify last event is RUN_FINISHED
|
|
906
|
+
expect(eventTypes[eventTypes.length - 1]).toBe(EventType.RUN_FINISHED);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
it("should emit reasoning events followed by text events", async () => {
|
|
910
|
+
const agent = new BasicAgent({
|
|
911
|
+
model: "openai/gpt-4o",
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
vi.mocked(streamText).mockReturnValue(
|
|
915
|
+
mockStreamTextResponse([
|
|
916
|
+
reasoningStart(),
|
|
917
|
+
reasoningDelta("thinking"),
|
|
918
|
+
reasoningEnd(),
|
|
919
|
+
textDelta("Hello"),
|
|
920
|
+
finish(),
|
|
921
|
+
]) as any,
|
|
922
|
+
);
|
|
923
|
+
|
|
924
|
+
const input: RunAgentInput = {
|
|
925
|
+
threadId: "thread1",
|
|
926
|
+
runId: "run1",
|
|
927
|
+
messages: [],
|
|
928
|
+
tools: [],
|
|
929
|
+
context: [],
|
|
930
|
+
state: {},
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
const events = await collectEvents(agent["run"](input));
|
|
934
|
+
const eventTypes = events.map((e: any) => e.type);
|
|
935
|
+
|
|
936
|
+
// Reasoning events should come before text events
|
|
937
|
+
const reasoningEndIdx = eventTypes.indexOf(EventType.REASONING_END);
|
|
938
|
+
const textChunkIdx = eventTypes.indexOf(EventType.TEXT_MESSAGE_CHUNK);
|
|
939
|
+
expect(reasoningEndIdx).toBeLessThan(textChunkIdx);
|
|
940
|
+
|
|
941
|
+
// Reasoning messageId should differ from text messageId
|
|
942
|
+
const reasoningEvent = events.find(
|
|
943
|
+
(e: any) => e.type === EventType.REASONING_START,
|
|
944
|
+
);
|
|
945
|
+
const textEvent = events.find(
|
|
946
|
+
(e: any) => e.type === EventType.TEXT_MESSAGE_CHUNK,
|
|
947
|
+
);
|
|
948
|
+
expect(reasoningEvent.messageId).not.toBe(textEvent.messageId);
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
it("should use provider-supplied reasoning id", async () => {
|
|
952
|
+
const agent = new BasicAgent({
|
|
953
|
+
model: "openai/gpt-4o",
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
vi.mocked(streamText).mockReturnValue(
|
|
957
|
+
mockStreamTextResponse([
|
|
958
|
+
reasoningStart("reasoning-msg-123"),
|
|
959
|
+
reasoningDelta("content"),
|
|
960
|
+
reasoningEnd(),
|
|
961
|
+
finish(),
|
|
962
|
+
]) as any,
|
|
963
|
+
);
|
|
964
|
+
|
|
965
|
+
const input: RunAgentInput = {
|
|
966
|
+
threadId: "thread1",
|
|
967
|
+
runId: "run1",
|
|
968
|
+
messages: [],
|
|
969
|
+
tools: [],
|
|
970
|
+
context: [],
|
|
971
|
+
state: {},
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
const events = await collectEvents(agent["run"](input));
|
|
975
|
+
|
|
976
|
+
const reasoningEvents = events.filter((e: any) =>
|
|
977
|
+
[
|
|
978
|
+
EventType.REASONING_START,
|
|
979
|
+
EventType.REASONING_MESSAGE_START,
|
|
980
|
+
EventType.REASONING_MESSAGE_CONTENT,
|
|
981
|
+
EventType.REASONING_MESSAGE_END,
|
|
982
|
+
EventType.REASONING_END,
|
|
983
|
+
].includes(e.type),
|
|
984
|
+
);
|
|
985
|
+
|
|
986
|
+
for (const event of reasoningEvents) {
|
|
987
|
+
expect(event.messageId).toBe("reasoning-msg-123");
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
it("should generate unique reasoningMessageId when provider returns id '0'", async () => {
|
|
992
|
+
const agent = new BasicAgent({
|
|
993
|
+
model: "openai/gpt-4o",
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
vi.mocked(streamText).mockReturnValue(
|
|
997
|
+
mockStreamTextResponse([
|
|
998
|
+
reasoningStart("0"),
|
|
999
|
+
reasoningDelta("content"),
|
|
1000
|
+
reasoningEnd(),
|
|
1001
|
+
finish(),
|
|
1002
|
+
]) as any,
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
const input: RunAgentInput = {
|
|
1006
|
+
threadId: "thread1",
|
|
1007
|
+
runId: "run1",
|
|
1008
|
+
messages: [],
|
|
1009
|
+
tools: [],
|
|
1010
|
+
context: [],
|
|
1011
|
+
state: {},
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
const events = await collectEvents(agent["run"](input));
|
|
1015
|
+
|
|
1016
|
+
const reasoningEvent = events.find(
|
|
1017
|
+
(e: any) => e.type === EventType.REASONING_START,
|
|
1018
|
+
);
|
|
1019
|
+
expect(reasoningEvent.messageId).not.toBe("0");
|
|
1020
|
+
expect(reasoningEvent.messageId).toMatch(
|
|
1021
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
|
|
1022
|
+
);
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
it("should handle empty reasoning content", async () => {
|
|
1026
|
+
const agent = new BasicAgent({
|
|
1027
|
+
model: "openai/gpt-4o",
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
vi.mocked(streamText).mockReturnValue(
|
|
1031
|
+
mockStreamTextResponse([
|
|
1032
|
+
reasoningStart(),
|
|
1033
|
+
reasoningDelta(""),
|
|
1034
|
+
reasoningEnd(),
|
|
1035
|
+
finish(),
|
|
1036
|
+
]) as any,
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
const input: RunAgentInput = {
|
|
1040
|
+
threadId: "thread1",
|
|
1041
|
+
runId: "run1",
|
|
1042
|
+
messages: [],
|
|
1043
|
+
tools: [],
|
|
1044
|
+
context: [],
|
|
1045
|
+
state: {},
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const events = await collectEvents(agent["run"](input));
|
|
1049
|
+
|
|
1050
|
+
const contentEvent = events.find(
|
|
1051
|
+
(e: any) => e.type === EventType.REASONING_MESSAGE_CONTENT,
|
|
1052
|
+
);
|
|
1053
|
+
expect(contentEvent).toMatchObject({ delta: "" });
|
|
1054
|
+
|
|
1055
|
+
// Full lifecycle should still complete
|
|
1056
|
+
const eventTypes = events.map((e: any) => e.type);
|
|
1057
|
+
expect(eventTypes).toContain(EventType.REASONING_START);
|
|
1058
|
+
expect(eventTypes).toContain(EventType.REASONING_MESSAGE_START);
|
|
1059
|
+
expect(eventTypes).toContain(EventType.REASONING_MESSAGE_END);
|
|
1060
|
+
expect(eventTypes).toContain(EventType.REASONING_END);
|
|
1061
|
+
expect(eventTypes).toContain(EventType.RUN_FINISHED);
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
it("should handle reasoning-only stream (no text output)", async () => {
|
|
1065
|
+
const agent = new BasicAgent({
|
|
1066
|
+
model: "openai/gpt-4o",
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
vi.mocked(streamText).mockReturnValue(
|
|
1070
|
+
mockStreamTextResponse([
|
|
1071
|
+
reasoningStart(),
|
|
1072
|
+
reasoningDelta("Deep thought"),
|
|
1073
|
+
reasoningEnd(),
|
|
1074
|
+
finish(),
|
|
1075
|
+
]) as any,
|
|
1076
|
+
);
|
|
1077
|
+
|
|
1078
|
+
const input: RunAgentInput = {
|
|
1079
|
+
threadId: "thread1",
|
|
1080
|
+
runId: "run1",
|
|
1081
|
+
messages: [],
|
|
1082
|
+
tools: [],
|
|
1083
|
+
context: [],
|
|
1084
|
+
state: {},
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
const events = await collectEvents(agent["run"](input));
|
|
1088
|
+
|
|
1089
|
+
// No TEXT_MESSAGE_CHUNK events
|
|
1090
|
+
const textEvents = events.filter(
|
|
1091
|
+
(e: any) => e.type === EventType.TEXT_MESSAGE_CHUNK,
|
|
1092
|
+
);
|
|
1093
|
+
expect(textEvents).toHaveLength(0);
|
|
1094
|
+
|
|
1095
|
+
// Reasoning events are present
|
|
1096
|
+
const reasoningContentEvents = events.filter(
|
|
1097
|
+
(e: any) => e.type === EventType.REASONING_MESSAGE_CONTENT,
|
|
1098
|
+
);
|
|
1099
|
+
expect(reasoningContentEvents).toHaveLength(1);
|
|
1100
|
+
expect(reasoningContentEvents[0]).toMatchObject({
|
|
1101
|
+
delta: "Deep thought",
|
|
1102
|
+
});
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it("should handle reasoning interleaved with tool calls", async () => {
|
|
1106
|
+
const agent = new BasicAgent({
|
|
1107
|
+
model: "openai/gpt-4o",
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
vi.mocked(streamText).mockReturnValue(
|
|
1111
|
+
mockStreamTextResponse([
|
|
1112
|
+
reasoningStart(),
|
|
1113
|
+
reasoningDelta("I need to call a tool"),
|
|
1114
|
+
reasoningEnd(),
|
|
1115
|
+
toolCallStreamingStart("call1", "testTool"),
|
|
1116
|
+
toolCallDelta("call1", '{"arg":"val"}'),
|
|
1117
|
+
toolCall("call1", "testTool", { arg: "val" }),
|
|
1118
|
+
toolResult("call1", "testTool", { result: "success" }),
|
|
1119
|
+
finish(),
|
|
1120
|
+
]) as any,
|
|
1121
|
+
);
|
|
1122
|
+
|
|
1123
|
+
const input: RunAgentInput = {
|
|
1124
|
+
threadId: "thread1",
|
|
1125
|
+
runId: "run1",
|
|
1126
|
+
messages: [],
|
|
1127
|
+
tools: [],
|
|
1128
|
+
context: [],
|
|
1129
|
+
state: {},
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
const events = await collectEvents(agent["run"](input));
|
|
1133
|
+
const eventTypes = events.map((e: any) => e.type);
|
|
1134
|
+
|
|
1135
|
+
// Reasoning events precede tool call events
|
|
1136
|
+
const reasoningEndIdx = eventTypes.indexOf(EventType.REASONING_END);
|
|
1137
|
+
const toolCallStartIdx = eventTypes.indexOf(EventType.TOOL_CALL_START);
|
|
1138
|
+
expect(reasoningEndIdx).toBeLessThan(toolCallStartIdx);
|
|
1139
|
+
|
|
1140
|
+
// Both lifecycles complete
|
|
1141
|
+
expect(eventTypes).toContain(EventType.REASONING_START);
|
|
1142
|
+
expect(eventTypes).toContain(EventType.REASONING_END);
|
|
1143
|
+
expect(eventTypes).toContain(EventType.TOOL_CALL_START);
|
|
1144
|
+
expect(eventTypes).toContain(EventType.TOOL_CALL_END);
|
|
1145
|
+
});
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
describe("Provider Options", () => {
|
|
1149
|
+
it("should pass providerOptions to streamText", async () => {
|
|
1150
|
+
const agent = new BasicAgent({
|
|
1151
|
+
model: "openai/gpt-4o",
|
|
1152
|
+
providerOptions: {
|
|
1153
|
+
openai: { reasoningEffort: "high", reasoningSummary: "detailed" },
|
|
1154
|
+
},
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
vi.mocked(streamText).mockReturnValue(
|
|
1158
|
+
mockStreamTextResponse([finish()]) as any,
|
|
1159
|
+
);
|
|
1160
|
+
|
|
1161
|
+
const input: RunAgentInput = {
|
|
1162
|
+
threadId: "thread1",
|
|
1163
|
+
runId: "run1",
|
|
1164
|
+
messages: [],
|
|
1165
|
+
tools: [],
|
|
1166
|
+
context: [],
|
|
1167
|
+
state: {},
|
|
1168
|
+
};
|
|
1169
|
+
|
|
1170
|
+
await collectEvents(agent["run"](input));
|
|
1171
|
+
|
|
1172
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
1173
|
+
expect(callArgs.providerOptions).toEqual({
|
|
1174
|
+
openai: { reasoningEffort: "high", reasoningSummary: "detailed" },
|
|
1175
|
+
});
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
it("should allow providerOptions override via forwardedProps when overridable", async () => {
|
|
1179
|
+
const agent = new BasicAgent({
|
|
1180
|
+
model: "openai/gpt-4o",
|
|
1181
|
+
providerOptions: {
|
|
1182
|
+
openai: { reasoningEffort: "low" },
|
|
1183
|
+
},
|
|
1184
|
+
overridableProperties: ["providerOptions"],
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
vi.mocked(streamText).mockReturnValue(
|
|
1188
|
+
mockStreamTextResponse([finish()]) as any,
|
|
1189
|
+
);
|
|
1190
|
+
|
|
1191
|
+
const input: RunAgentInput = {
|
|
1192
|
+
threadId: "thread1",
|
|
1193
|
+
runId: "run1",
|
|
1194
|
+
messages: [],
|
|
1195
|
+
tools: [],
|
|
1196
|
+
context: [],
|
|
1197
|
+
state: {},
|
|
1198
|
+
forwardedProps: {
|
|
1199
|
+
providerOptions: {
|
|
1200
|
+
openai: { reasoningEffort: "high" },
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
await collectEvents(agent["run"](input));
|
|
1206
|
+
|
|
1207
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
1208
|
+
expect(callArgs.providerOptions).toEqual({
|
|
1209
|
+
openai: { reasoningEffort: "high" },
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
it("should NOT allow providerOptions override when not in overridableProperties", async () => {
|
|
1214
|
+
const agent = new BasicAgent({
|
|
1215
|
+
model: "openai/gpt-4o",
|
|
1216
|
+
providerOptions: {
|
|
1217
|
+
openai: { reasoningEffort: "low" },
|
|
1218
|
+
},
|
|
1219
|
+
overridableProperties: [],
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
vi.mocked(streamText).mockReturnValue(
|
|
1223
|
+
mockStreamTextResponse([finish()]) as any,
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
const input: RunAgentInput = {
|
|
1227
|
+
threadId: "thread1",
|
|
1228
|
+
runId: "run1",
|
|
1229
|
+
messages: [],
|
|
1230
|
+
tools: [],
|
|
1231
|
+
context: [],
|
|
1232
|
+
state: {},
|
|
1233
|
+
forwardedProps: {
|
|
1234
|
+
providerOptions: {
|
|
1235
|
+
openai: { reasoningEffort: "high" },
|
|
1236
|
+
},
|
|
1237
|
+
},
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
await collectEvents(agent["run"](input));
|
|
1241
|
+
|
|
1242
|
+
const callArgs = vi.mocked(streamText).mock.calls[0][0];
|
|
1243
|
+
expect(callArgs.providerOptions).toEqual({
|
|
1244
|
+
openai: { reasoningEffort: "low" },
|
|
1245
|
+
});
|
|
1246
|
+
});
|
|
1247
|
+
});
|
|
821
1248
|
});
|
|
@@ -103,6 +103,38 @@ export function toolResult(
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Helper to create a reasoning-start event
|
|
108
|
+
*/
|
|
109
|
+
export function reasoningStart(id?: string): MockStreamEvent {
|
|
110
|
+
const event: MockStreamEvent = {
|
|
111
|
+
type: "reasoning-start",
|
|
112
|
+
};
|
|
113
|
+
if (id !== undefined) {
|
|
114
|
+
event.id = id;
|
|
115
|
+
}
|
|
116
|
+
return event;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Helper to create a reasoning-delta event
|
|
121
|
+
*/
|
|
122
|
+
export function reasoningDelta(text: string): MockStreamEvent {
|
|
123
|
+
return {
|
|
124
|
+
type: "reasoning-delta",
|
|
125
|
+
text,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Helper to create a reasoning-end event
|
|
131
|
+
*/
|
|
132
|
+
export function reasoningEnd(): MockStreamEvent {
|
|
133
|
+
return {
|
|
134
|
+
type: "reasoning-end",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
106
138
|
/**
|
|
107
139
|
* Helper to create a finish event
|
|
108
140
|
*/
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,11 @@ import {
|
|
|
4
4
|
RunAgentInput,
|
|
5
5
|
EventType,
|
|
6
6
|
Message,
|
|
7
|
+
ReasoningEndEvent,
|
|
8
|
+
ReasoningMessageContentEvent,
|
|
9
|
+
ReasoningMessageEndEvent,
|
|
10
|
+
ReasoningMessageStartEvent,
|
|
11
|
+
ReasoningStartEvent,
|
|
7
12
|
RunFinishedEvent,
|
|
8
13
|
RunStartedEvent,
|
|
9
14
|
TextMessageChunkEvent,
|
|
@@ -60,7 +65,8 @@ export type OverridableProperty =
|
|
|
60
65
|
| "stopSequences"
|
|
61
66
|
| "seed"
|
|
62
67
|
| "maxRetries"
|
|
63
|
-
| "prompt"
|
|
68
|
+
| "prompt"
|
|
69
|
+
| "providerOptions";
|
|
64
70
|
|
|
65
71
|
/**
|
|
66
72
|
* Supported model identifiers for BuiltInAgent
|
|
@@ -572,6 +578,11 @@ export interface BuiltInAgentConfiguration {
|
|
|
572
578
|
* Default: false
|
|
573
579
|
*/
|
|
574
580
|
forwardDeveloperMessages?: boolean;
|
|
581
|
+
/**
|
|
582
|
+
* Provider-specific options passed to the model (e.g., OpenAI reasoningEffort).
|
|
583
|
+
* Example: `{ openai: { reasoningEffort: "high" } }`
|
|
584
|
+
*/
|
|
585
|
+
providerOptions?: Record<string, any>;
|
|
575
586
|
}
|
|
576
587
|
|
|
577
588
|
export class BuiltInAgent extends AbstractAgent {
|
|
@@ -683,6 +694,7 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
683
694
|
frequencyPenalty: this.config.frequencyPenalty,
|
|
684
695
|
stopSequences: this.config.stopSequences,
|
|
685
696
|
seed: this.config.seed,
|
|
697
|
+
providerOptions: this.config.providerOptions,
|
|
686
698
|
maxRetries: this.config.maxRetries,
|
|
687
699
|
};
|
|
688
700
|
|
|
@@ -773,6 +785,20 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
773
785
|
) {
|
|
774
786
|
streamTextParams.maxRetries = props.maxRetries;
|
|
775
787
|
}
|
|
788
|
+
if (
|
|
789
|
+
props.providerOptions !== undefined &&
|
|
790
|
+
this.canOverride("providerOptions")
|
|
791
|
+
) {
|
|
792
|
+
if (
|
|
793
|
+
typeof props.providerOptions === "object" &&
|
|
794
|
+
props.providerOptions !== null
|
|
795
|
+
) {
|
|
796
|
+
streamTextParams.providerOptions = props.providerOptions as Record<
|
|
797
|
+
string,
|
|
798
|
+
any
|
|
799
|
+
>;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
776
802
|
}
|
|
777
803
|
|
|
778
804
|
// Set up MCP clients if configured and process the stream
|
|
@@ -865,6 +891,7 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
865
891
|
});
|
|
866
892
|
|
|
867
893
|
let messageId = randomUUID();
|
|
894
|
+
let reasoningMessageId = randomUUID();
|
|
868
895
|
|
|
869
896
|
const toolCallStates = new Map<
|
|
870
897
|
string,
|
|
@@ -888,7 +915,7 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
888
915
|
// Process fullStream events
|
|
889
916
|
for await (const part of response.fullStream) {
|
|
890
917
|
switch (part.type) {
|
|
891
|
-
case "abort":
|
|
918
|
+
case "abort": {
|
|
892
919
|
const abortEndEvent: RunFinishedEvent = {
|
|
893
920
|
type: EventType.RUN_FINISHED,
|
|
894
921
|
threadId: input.threadId,
|
|
@@ -900,7 +927,50 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
900
927
|
// Complete the observable
|
|
901
928
|
subscriber.complete();
|
|
902
929
|
break;
|
|
903
|
-
|
|
930
|
+
}
|
|
931
|
+
case "reasoning-start": {
|
|
932
|
+
// New text message starting - use the SDK-provided id
|
|
933
|
+
// Use randomUUID() if part.id is falsy or "0" to prevent message merging issues
|
|
934
|
+
const providedId = "id" in part ? part.id : undefined;
|
|
935
|
+
if (providedId && providedId !== "0") {
|
|
936
|
+
reasoningMessageId = providedId as typeof reasoningMessageId;
|
|
937
|
+
}
|
|
938
|
+
const reasoningStartEvent: ReasoningStartEvent = {
|
|
939
|
+
type: EventType.REASONING_START,
|
|
940
|
+
messageId: reasoningMessageId,
|
|
941
|
+
};
|
|
942
|
+
subscriber.next(reasoningStartEvent);
|
|
943
|
+
const reasoningMessageStart: ReasoningMessageStartEvent = {
|
|
944
|
+
type: EventType.REASONING_MESSAGE_START,
|
|
945
|
+
messageId: reasoningMessageId,
|
|
946
|
+
role: "reasoning",
|
|
947
|
+
};
|
|
948
|
+
subscriber.next(reasoningMessageStart);
|
|
949
|
+
break;
|
|
950
|
+
}
|
|
951
|
+
case "reasoning-delta": {
|
|
952
|
+
const reasoningDeltaEvent: ReasoningMessageContentEvent = {
|
|
953
|
+
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
954
|
+
messageId: reasoningMessageId,
|
|
955
|
+
delta:
|
|
956
|
+
("text" in part ? part.text : (part as any).delta) ?? "",
|
|
957
|
+
};
|
|
958
|
+
subscriber.next(reasoningDeltaEvent);
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
case "reasoning-end": {
|
|
962
|
+
const reasoningMessageEnd: ReasoningMessageEndEvent = {
|
|
963
|
+
type: EventType.REASONING_MESSAGE_END,
|
|
964
|
+
messageId: reasoningMessageId,
|
|
965
|
+
};
|
|
966
|
+
subscriber.next(reasoningMessageEnd);
|
|
967
|
+
const reasoningEndEvent: ReasoningEndEvent = {
|
|
968
|
+
type: EventType.REASONING_END,
|
|
969
|
+
messageId: reasoningMessageId,
|
|
970
|
+
};
|
|
971
|
+
subscriber.next(reasoningEndEvent);
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
904
974
|
case "tool-input-start": {
|
|
905
975
|
const toolCallId = part.id;
|
|
906
976
|
const state = ensureToolCallState(toolCallId);
|
|
@@ -1057,7 +1127,7 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
1057
1127
|
break;
|
|
1058
1128
|
}
|
|
1059
1129
|
|
|
1060
|
-
case "finish":
|
|
1130
|
+
case "finish": {
|
|
1061
1131
|
// Emit run finished event
|
|
1062
1132
|
const finishedEvent: RunFinishedEvent = {
|
|
1063
1133
|
type: EventType.RUN_FINISHED,
|
|
@@ -1070,6 +1140,7 @@ export class BuiltInAgent extends AbstractAgent {
|
|
|
1070
1140
|
// Complete the observable
|
|
1071
1141
|
subscriber.complete();
|
|
1072
1142
|
break;
|
|
1143
|
+
}
|
|
1073
1144
|
|
|
1074
1145
|
case "error": {
|
|
1075
1146
|
if (abortController.signal.aborted) {
|