Skip to content

Commit 60071be

Browse files
committed
feat(server-sdk,server-shared): improved context update, renamed from context message, added output event, source
1 parent 340c22b commit 60071be

File tree

4 files changed

+69
-37
lines changed

4 files changed

+69
-37
lines changed

packages/server-sdk/src/client.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { WebSocketBaseEvent, WebSocketEvent, WebSocketEvents } from '@proj-airi/server-shared/types'
1+
import type { WebSocketBaseEvent, WebSocketEvent, WebSocketEventOptionalSource, WebSocketEvents, WebSocketEventSource } from '@proj-airi/server-shared/types'
22

33
import WebSocket from 'crossws/websocket'
44

@@ -212,6 +212,7 @@ export class Client<C = undefined> {
212212
for (const listener of listeners) {
213213
executions.push(Promise.resolve(listener(data as any)))
214214
}
215+
215216
await Promise.allSettled(executions)
216217
}
217218
catch (err) {
@@ -252,9 +253,9 @@ export class Client<C = undefined> {
252253
}
253254
}
254255

255-
send(data: WebSocketEvent<C>): void {
256+
send(data: WebSocketEventOptionalSource<C>): void {
256257
if (this.websocket && this.connected) {
257-
this.websocket.send(JSON.stringify(data))
258+
this.websocket.send(JSON.stringify({ source: this.opts.name as WebSocketEventSource | string, ...data } as WebSocketEvent<C>))
258259
}
259260
}
260261

packages/server-sdk/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './client'
22
export type * from '@proj-airi/server-shared/types'
3+
export { ContextUpdateStrategy, WebSocketEventSource } from '@proj-airi/server-shared/types'

packages/server-shared/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,9 @@
3131
"dev": "pnpm run build",
3232
"build": "tsdown",
3333
"typecheck": "tsc --noEmit"
34+
},
35+
"dependencies": {
36+
"@xsai/shared": "catalog:",
37+
"@xsai/shared-chat": "catalog:"
3438
}
3539
}
Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { AssistantMessage, ToolMessage } from '@xsai/shared-chat'
2+
13
export interface DiscordGuildMember {
24
nickname: string
35
displayName: string
@@ -10,48 +12,66 @@ export interface Discord {
1012
channelId?: string
1113
}
1214

15+
export enum WebSocketEventSource {
16+
Server = 'proj-airi:server-runtime',
17+
StageWeb = 'proj-airi:stage-web',
18+
StageTamagotchi = 'proj-airi:stage-tamagotchi',
19+
}
20+
1321
interface InputSource {
14-
browser: string
15-
discord: Discord
22+
'stage-web': string
23+
'stage-tamagotchi': string
24+
'discord': Discord
25+
}
26+
27+
interface OutputSource {
28+
'gen-ai-model-chat': string
29+
}
30+
31+
export enum ContextUpdateStrategy {
32+
ReplaceSelf = 'replace-self',
33+
AppendSelf = 'append-self',
34+
}
35+
36+
export interface ContextUpdateDestinationAll {
37+
all: true
38+
}
39+
40+
export interface ContextUpdateDestinationList {
41+
include?: Array<string>
42+
exclude?: Array<string>
1643
}
1744

18-
export type ContextSource
19-
= | 'text'
20-
| 'stt'
21-
| 'vision'
22-
| 'llm'
23-
| 'server-channel'
24-
| 'plugin'
25-
| 'system'
26-
27-
export interface ContextMessage<Payload = unknown, Meta = Record<string, unknown>> {
28-
/**
29-
* Session identifier so UIs can group conversations from multiple windows/devices.
30-
*/
31-
sessionId: string
32-
/**
33-
* Unix timestamp in milliseconds.
34-
*/
35-
ts: number
36-
role: 'user' | 'assistant' | 'system' | 'error' | 'tool'
37-
source: ContextSource
38-
/**
39-
* The actual payload being carried. Keep this generic so different inputs (text, stt, vision)
40-
* can share the same envelope.
41-
*/
42-
payload: Payload
43-
meta?: Meta
45+
export type ContextUpdateDestinationFilter
46+
= | ContextUpdateDestinationAll
47+
| ContextUpdateDestinationList
48+
49+
export interface ContextUpdate<
50+
Metadata extends Record<string, any> = Record<string, unknown>,
51+
// eslint-disable-next-line ts/no-unnecessary-type-constraint
52+
Content extends any = undefined,
53+
> {
54+
strategy: ContextUpdateStrategy
55+
text: string
56+
content?: Content
57+
destinations?: Array<string> | ContextUpdateDestinationFilter
58+
metadata?: Metadata
4459
}
4560

46-
export interface WebSocketBaseEvent<T, D> {
61+
export interface WebSocketBaseEvent<T, D, S extends string = string> {
4762
type: T
4863
data: D
64+
source: WebSocketEventSource | S
4965
}
5066

5167
export type WithInputSource<Source extends keyof InputSource> = {
5268
[S in Source]: InputSource[S]
5369
}
5470

71+
export type WithOutputSource<Source extends keyof OutputSource> = {
72+
[S in Source]: OutputSource[S]
73+
}
74+
5575
// Thanks to:
5676
//
5777
// A little hack for creating extensible discriminated unions : r/typescript
@@ -80,17 +100,23 @@ export interface WebSocketEvents<C = undefined> {
80100
}
81101
'input:text': {
82102
text: string
83-
} & Partial<WithInputSource<'browser' | 'discord'>>
103+
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>>
84104
'input:text:voice': {
85105
transcription: string
86-
} & Partial<WithInputSource<'browser' | 'discord'>>
106+
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>>
87107
'input:voice': {
88108
audio: ArrayBuffer
89-
} & Partial<WithInputSource<'browser' | 'discord'>>
90-
'vscode:context': C
91-
'context:update': ContextMessage
109+
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>>
110+
'output:gen-ai:chat:message': {
111+
messages: Array<AssistantMessage | ToolMessage>
112+
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>> & Partial<WithOutputSource<'gen-ai-model-chat'>>
113+
'context:update': ContextUpdate
92114
}
93115

94116
export type WebSocketEvent<C = undefined> = {
95117
[K in keyof WebSocketEvents<C>]: WebSocketBaseEvent<K, WebSocketEvents<C>[K]>;
96118
}[keyof WebSocketEvents<C>]
119+
120+
export type WebSocketEventOptionalSource<C = undefined> = {
121+
[K in keyof WebSocketEvents<C>]: Omit<WebSocketBaseEvent<K, WebSocketEvents<C>[K]>, 'source'> & Partial<Pick<WebSocketBaseEvent<K, WebSocketEvents<C>[K]>, 'source'>>;
122+
}[keyof WebSocketEvents<C>]

0 commit comments

Comments
 (0)