Skip to content

Commit 26d85af

Browse files
authored
Merge pull request #173 from anam-org/sam/thinking_models
feat: handle webrtc reasoning events
2 parents 91ce470 + ce0f26e commit 26d85af

15 files changed

Lines changed: 132 additions & 1 deletion

src/AnamClient.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
MessageHistoryClient,
1919
PublicEventEmitter,
2020
StreamingClient,
21+
ReasoningHistoryClient,
2122
} from './modules';
2223
import {
2324
AnamClientOptions,
@@ -38,6 +39,7 @@ export default class AnamClient {
3839
private internalEventEmitter: InternalEventEmitter;
3940

4041
private readonly messageHistoryClient: MessageHistoryClient;
42+
private readonly reasoningHistoryClient: ReasoningHistoryClient;
4143

4244
private personaConfig: PersonaConfig | undefined;
4345
private clientOptions: AnamClientOptions | undefined;
@@ -104,6 +106,11 @@ export default class AnamClient {
104106
this.publicEventEmitter,
105107
this.internalEventEmitter,
106108
);
109+
110+
this.reasoningHistoryClient = new ReasoningHistoryClient(
111+
this.publicEventEmitter,
112+
this.internalEventEmitter,
113+
);
107114
}
108115

109116
private decodeJwt(token: string): any {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { PublicEventEmitter, InternalEventEmitter } from '.';
2+
import {
3+
AnamEvent,
4+
InternalEvent,
5+
ReasoningMessage,
6+
ReasoningStreamEvent,
7+
WebRtcReasoningTextMessageEvent,
8+
} from '../types';
9+
10+
export class ReasoningHistoryClient {
11+
private publicEventEmitter: PublicEventEmitter;
12+
private internalEventEmitter: InternalEventEmitter;
13+
14+
private reasoning_messages: ReasoningMessage[] = [];
15+
constructor(
16+
publicEventEmitter: PublicEventEmitter,
17+
internalEventEmitter: InternalEventEmitter,
18+
) {
19+
this.publicEventEmitter = publicEventEmitter;
20+
this.internalEventEmitter = internalEventEmitter;
21+
// register for events
22+
this.internalEventEmitter.addListener(
23+
InternalEvent.WEBRTC_REASONING_TEXT_MESSAGE_RECEIVED,
24+
this.processWebRtcReasoningTextMessageEvent.bind(this),
25+
);
26+
}
27+
28+
private webRtcTextMessageEventToReasoningStreamEvent(
29+
event: WebRtcReasoningTextMessageEvent,
30+
): ReasoningStreamEvent {
31+
return {
32+
id: `${event.role}::${event.message_id}`,
33+
content: event.content,
34+
endOfThought: event.end_of_thought,
35+
role: event.role,
36+
};
37+
}
38+
39+
private processWebRtcReasoningTextMessageEvent(
40+
event: WebRtcReasoningTextMessageEvent,
41+
): void {
42+
const ReasoningStreamEvent: ReasoningStreamEvent =
43+
this.webRtcTextMessageEventToReasoningStreamEvent(event);
44+
45+
this.publicEventEmitter.emit(
46+
AnamEvent.REASONING_STREAM_EVENT_RECEIVED,
47+
ReasoningStreamEvent,
48+
);
49+
50+
const message: ReasoningMessage = {
51+
id: ReasoningStreamEvent.id,
52+
content: ReasoningStreamEvent.content,
53+
role: ReasoningStreamEvent.role,
54+
};
55+
56+
const existingMessageIndex = this.reasoning_messages.findIndex(
57+
(m) => m.id === message.id,
58+
);
59+
if (existingMessageIndex !== -1) {
60+
// update existing message
61+
const existingMessage = this.reasoning_messages[existingMessageIndex];
62+
existingMessage.content += message.content;
63+
this.reasoning_messages[existingMessageIndex] = existingMessage;
64+
} else {
65+
// new message
66+
this.reasoning_messages.push(message);
67+
}
68+
69+
if (ReasoningStreamEvent.endOfThought) {
70+
this.publicEventEmitter.emit(
71+
AnamEvent.REASONING_HISTORY_UPDATED,
72+
this.reasoning_messages,
73+
);
74+
}
75+
}
76+
}

src/modules/StreamingClient.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
StreamingClientOptions,
2626
WebRtcClientToolEvent,
2727
WebRtcTextMessageEvent,
28+
WebRtcReasoningTextMessageEvent,
2829
} from '../types';
2930
import { AgentAudioInputStream } from '../types/AgentAudioInputStream';
3031
import { TalkMessageStream } from '../types/TalkMessageStream';
@@ -693,6 +694,7 @@ export class StreamingClient {
693694
message.data as WebRtcTextMessageEvent,
694695
);
695696
break;
697+
696698
case DataChannelMessage.CLIENT_TOOL_EVENT:
697699
const webRtcToolEvent = message.data as WebRtcClientToolEvent;
698700

@@ -709,6 +711,12 @@ export class StreamingClient {
709711
clientToolEvent,
710712
);
711713
break;
714+
case DataChannelMessage.REASONING_TEXT:
715+
this.internalEventEmitter.emit(
716+
InternalEvent.WEBRTC_REASONING_TEXT_MESSAGE_RECEIVED,
717+
message.data as WebRtcReasoningTextMessageEvent,
718+
);
719+
break;
712720
// Unknown message types are silently ignored to maintain forward compatibility
713721
default:
714722
break;

src/modules/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { MessageHistoryClient } from './MessageHistoryClient';
66
export { PublicEventEmitter } from './PublicEventEmitter';
77
export { StreamingClient } from './StreamingClient';
88
export { ToolCallManager } from './ToolCallManager';
9+
export { ReasoningHistoryClient } from './ReasoningHistoryClient';

src/types/AgentAudioInputStream.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ export class AgentAudioInputStream {
6262
private arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {
6363
const bytes =
6464
buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
65-
const binary = Array.from(bytes, byte => String.fromCharCode(byte)).join('');
65+
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join(
66+
'',
67+
);
6668
return btoa(binary);
6769
}
6870
}

src/types/events/internal/InternalEvent.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export enum InternalEvent {
33
SIGNAL_MESSAGE_RECEIVED = 'SIGNAL_MESSAGE_RECEIVED',
44
WEBRTC_CHAT_MESSAGE_RECEIVED = 'WEBRTC_CHAT_MESSAGE_RECEIVED',
55
WEBRTC_CLIENT_TOOL_EVENT_RECEIVED = 'WEBRTC_CLIENT_TOOL_EVENT_RECEIVED',
6+
WEBRTC_REASONING_TEXT_MESSAGE_RECEIVED = 'WEBRTC_REASONING_TEXT_MESSAGE_RECEIVED',
67
}

src/types/events/internal/InternalEventCallbacks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
SignalMessage,
44
WebRtcTextMessageEvent,
55
WebRtcClientToolEvent,
6+
WebRtcReasoningTextMessageEvent,
67
} from '../../index';
78

89
export type InternalEventCallbacks = {
@@ -16,4 +17,7 @@ export type InternalEventCallbacks = {
1617
[InternalEvent.WEBRTC_CLIENT_TOOL_EVENT_RECEIVED]: (
1718
webRtcToolEvent: WebRtcClientToolEvent,
1819
) => void;
20+
[InternalEvent.WEBRTC_REASONING_TEXT_MESSAGE_RECEIVED]: (
21+
message: WebRtcReasoningTextMessageEvent,
22+
) => void;
1923
};

src/types/events/public/AnamEvent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ export enum AnamEvent {
1515
MIC_PERMISSION_DENIED = 'MIC_PERMISSION_DENIED',
1616
INPUT_AUDIO_DEVICE_CHANGED = 'INPUT_AUDIO_DEVICE_CHANGED',
1717
CLIENT_TOOL_EVENT_RECEIVED = 'CLIENT_TOOL_EVENT_RECEIVED',
18+
REASONING_HISTORY_UPDATED = 'REASONING_HISTORY_UPDATED',
19+
REASONING_STREAM_EVENT_RECEIVED = 'REASONING_STREAM_EVENT_RECEIVED',
1820
}

src/types/events/public/EventCallbacks.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
MessageStreamEvent,
55
AnamEvent,
66
ClientToolEvent,
7+
ReasoningMessage,
8+
ReasoningStreamEvent,
79
} from '../../index';
810

911
export type EventCallbacks = {
@@ -30,4 +32,10 @@ export type EventCallbacks = {
3032
[AnamEvent.CLIENT_TOOL_EVENT_RECEIVED]: (
3133
clientToolEvent: ClientToolEvent,
3234
) => void;
35+
[AnamEvent.REASONING_HISTORY_UPDATED]: (
36+
thoughtMessages: ReasoningMessage[],
37+
) => void;
38+
[AnamEvent.REASONING_STREAM_EVENT_RECEIVED]: (
39+
thoughtEvent: ReasoningStreamEvent,
40+
) => void;
3341
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface ReasoningMessage {
2+
id: string;
3+
content: string;
4+
role: string;
5+
}

0 commit comments

Comments
 (0)