feat(chat-ws): add versioned Eventa protocols (#2308)
Signed-off-by: RainbowBird <git@luoling.moe>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# @proj-airi/server-sdk-shared
|
||||
|
||||
Eventa contracts for the hosted chat WebSocket.
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
ni @proj-airi/server-sdk-shared -D
|
||||
pnpm i @proj-airi/server-sdk-shared -D
|
||||
```
|
||||
|
||||
```typescript
|
||||
import type { WireMessage } from '@proj-airi/server-sdk-shared'
|
||||
|
||||
import { newMessages, pullMessages, sendMessages } from '@proj-airi/server-sdk-shared'
|
||||
```
|
||||
|
||||
The package uses Eventa `1.0.0-beta.15`. Its WebSocket adapter accepts beta.13
|
||||
`id/type/payload` envelopes and sends these fields with current envelopes.
|
||||
|
||||
`/ws/chat` keeps query-token authentication for deployed clients. `/ws/v2/chat`
|
||||
authenticates after the WebSocket opens.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](../../../LICENSE)
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@proj-airi/server-sdk-shared",
|
||||
"type": "module",
|
||||
"version": "0.11.3",
|
||||
"private": true,
|
||||
"description": "Eventa contracts for the hosted chat WebSocket",
|
||||
"author": {
|
||||
"name": "Moeru AI Project AIRI Team",
|
||||
"email": "airi@moeru.ai",
|
||||
"url": "https://github.com/moeru-ai"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moeru-ai/airi.git",
|
||||
"directory": "server/packages/server-sdk-shared"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"./v2": {
|
||||
"types": "./dist/v2.d.mts",
|
||||
"default": "./dist/v2.mjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts",
|
||||
"files": [
|
||||
"README.md",
|
||||
"dist",
|
||||
"package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "pnpm run build",
|
||||
"build": "tsdown",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@moeru/eventa": "catalog:",
|
||||
"valibot": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import * as v from 'valibot'
|
||||
|
||||
const NonEmptyStringSchema = v.pipe(v.string(), v.minLength(1))
|
||||
|
||||
const SendMessageSchema = v.object({
|
||||
id: NonEmptyStringSchema,
|
||||
role: v.string(),
|
||||
content: v.string(),
|
||||
})
|
||||
|
||||
export const SendMessagesRequestSchema = v.object({
|
||||
chatId: NonEmptyStringSchema,
|
||||
messages: v.array(SendMessageSchema),
|
||||
})
|
||||
|
||||
export const PullMessagesRequestSchema = v.object({
|
||||
chatId: NonEmptyStringSchema,
|
||||
afterSeq: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
||||
limit: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1))),
|
||||
})
|
||||
|
||||
export interface WireMessage {
|
||||
id: string
|
||||
chatId: string
|
||||
senderId: string | null
|
||||
role: 'system' | 'user' | 'assistant' | 'tool' | 'error'
|
||||
content: string
|
||||
seq: number
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
export type MessageRole = WireMessage['role']
|
||||
|
||||
export type SendMessagesRequest = v.InferOutput<typeof SendMessagesRequestSchema>
|
||||
|
||||
export interface SendMessagesResponse {
|
||||
seq: number
|
||||
}
|
||||
|
||||
export type PullMessagesRequest = v.InferOutput<typeof PullMessagesRequestSchema>
|
||||
|
||||
export interface PullMessagesResponse {
|
||||
messages: WireMessage[]
|
||||
seq: number
|
||||
}
|
||||
|
||||
export interface NewMessagesPayload {
|
||||
chatId: string
|
||||
messages: WireMessage[]
|
||||
fromSeq: number
|
||||
toSeq: number
|
||||
}
|
||||
|
||||
/** Parses a `chat:send-messages` payload at the WebSocket boundary. */
|
||||
export function parseSendMessagesRequest(request: unknown): SendMessagesRequest {
|
||||
return v.parse(SendMessagesRequestSchema, request)
|
||||
}
|
||||
|
||||
/** Parses a `chat:pull-messages` payload at the WebSocket boundary. */
|
||||
export function parsePullMessagesRequest(request: unknown): PullMessagesRequest {
|
||||
return v.parse(PullMessagesRequestSchema, request)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './v2'
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { NewMessagesPayload, PullMessagesRequest, PullMessagesResponse, SendMessagesRequest, SendMessagesResponse } from './chat'
|
||||
|
||||
import { defineInvokeEventa, defineOutboundEventa } from '@moeru/eventa'
|
||||
|
||||
export type {
|
||||
MessageRole,
|
||||
NewMessagesPayload,
|
||||
PullMessagesRequest,
|
||||
PullMessagesResponse,
|
||||
SendMessagesRequest,
|
||||
SendMessagesResponse,
|
||||
WireMessage,
|
||||
} from './chat'
|
||||
export {
|
||||
parsePullMessagesRequest,
|
||||
parseSendMessagesRequest,
|
||||
PullMessagesRequestSchema,
|
||||
SendMessagesRequestSchema,
|
||||
} from './chat'
|
||||
|
||||
export const sendMessages = defineInvokeEventa<SendMessagesResponse, SendMessagesRequest>('chat:send-messages')
|
||||
export const pullMessages = defineInvokeEventa<PullMessagesResponse, PullMessagesRequest>('chat:pull-messages')
|
||||
export const newMessages = defineOutboundEventa<NewMessagesPayload>('chat:new-messages')
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from 'tsdown'
|
||||
|
||||
export default defineConfig({
|
||||
entry: {
|
||||
index: 'src/index.ts',
|
||||
v2: 'src/v2.ts',
|
||||
},
|
||||
sourcemap: true,
|
||||
unused: true,
|
||||
inlineOnly: false,
|
||||
})
|
||||
Reference in New Issue
Block a user