@redhat-cloud-services/ai-client-common 0.3.0 → 0.3.2
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/package.json +2 -2
- package/src/{index.ts → index.d.ts} +1 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/ai-client-common.d.ts +249 -0
- package/src/lib/ai-client-common.d.ts.map +1 -0
- package/src/lib/ai-client-common.js +25 -0
- package/src/lib/ai-client-common.js.map +1 -0
- package/.eslintrc.json +0 -32
- package/jest.config.ts +0 -10
- package/project.json +0 -34
- package/src/lib/ai-client-common.spec.ts +0 -460
- package/src/lib/ai-client-common.ts +0 -301
- package/tsconfig.json +0 -23
- package/tsconfig.lib.json +0 -12
- package/tsconfig.spec.json +0 -15
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/ai-client-common/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./lib/ai-client-common"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/ai-client-common/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yDAAuC"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic interface for custom fetch implementation that must be injected into AI clients.
|
|
3
|
+
* Mirrors exactly the native browser fetch interface for maximum compatibility.
|
|
4
|
+
*/
|
|
5
|
+
export interface IFetchFunction {
|
|
6
|
+
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Base configuration interface for all AI clients
|
|
10
|
+
*/
|
|
11
|
+
export interface IBaseClientConfig<TChunk = unknown> {
|
|
12
|
+
/**
|
|
13
|
+
* The base URL for the API
|
|
14
|
+
*/
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
/**
|
|
17
|
+
* Custom fetch implementation for making HTTP requests
|
|
18
|
+
* Must include authentication headers (Bearer token) if needed
|
|
19
|
+
*/
|
|
20
|
+
fetchFunction: IFetchFunction;
|
|
21
|
+
/**
|
|
22
|
+
* Default streaming handler for the client
|
|
23
|
+
* Used when sendMessage is called with stream=true but no streamingHandler provided
|
|
24
|
+
* Individual requests can override this by providing their own streamingHandler
|
|
25
|
+
*/
|
|
26
|
+
defaultStreamingHandler?: IStreamingHandler<TChunk>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Standard request options that can be passed to individual client methods
|
|
30
|
+
*/
|
|
31
|
+
export interface IRequestOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Custom headers for this specific request
|
|
34
|
+
*/
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* AbortSignal to cancel the request
|
|
38
|
+
*/
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Common interface that all AI clients must implement
|
|
43
|
+
* Provides a standardized way to interact with different AI services
|
|
44
|
+
*/
|
|
45
|
+
export interface IAIClient<AP extends Record<string, unknown> = Record<string, unknown>> {
|
|
46
|
+
/**
|
|
47
|
+
* Initialize the client and return the initial conversation ID
|
|
48
|
+
* This method is called once when the client is first used by a state manager
|
|
49
|
+
* @returns Promise that resolves to the initial conversation ID
|
|
50
|
+
*/
|
|
51
|
+
init(): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Send a message to the AI service
|
|
54
|
+
* @param conversationId - The conversation ID to send the message to
|
|
55
|
+
* @param message - The message content to send
|
|
56
|
+
* @param options - Optional configuration for the request
|
|
57
|
+
* @returns Promise that resolves to the AI's response
|
|
58
|
+
*/
|
|
59
|
+
sendMessage<TChunk = unknown>(conversationId: string, message: string, options?: ISendMessageOptions<TChunk>): Promise<TChunk | IMessageResponse<AP> | void>;
|
|
60
|
+
/**
|
|
61
|
+
* Get the default streaming handler for this client (if any)
|
|
62
|
+
* @returns The default streaming handler or undefined if not configured
|
|
63
|
+
*/
|
|
64
|
+
getDefaultStreamingHandler?<TChunk = unknown>(): IStreamingHandler<TChunk> | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Get the conversation history for a specific conversation
|
|
67
|
+
* @param conversationId - The conversation ID to retrieve history for
|
|
68
|
+
* @param options - Optional request configuration
|
|
69
|
+
* @returns Promise that resolves to the conversation history
|
|
70
|
+
*/
|
|
71
|
+
getConversationHistory(conversationId: string, options?: IRequestOptions): Promise<IConversationHistoryResponse<AP>>;
|
|
72
|
+
/**
|
|
73
|
+
* Perform a health check on the AI service
|
|
74
|
+
* @param options - Optional request configuration
|
|
75
|
+
* @returns Promise that resolves to health status information
|
|
76
|
+
*/
|
|
77
|
+
healthCheck(options?: IRequestOptions): Promise<unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* Get the current status of the AI service
|
|
80
|
+
* @param options - Optional request configuration
|
|
81
|
+
* @returns Promise that resolves to service status information
|
|
82
|
+
*/
|
|
83
|
+
getServiceStatus?(options?: IRequestOptions): Promise<unknown>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Base error class for all AI client errors
|
|
87
|
+
*/
|
|
88
|
+
export declare class AIClientError extends Error {
|
|
89
|
+
status: number;
|
|
90
|
+
statusText: string;
|
|
91
|
+
data?: unknown | undefined;
|
|
92
|
+
constructor(status: number, statusText: string, message: string, data?: unknown | undefined);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Validation error class for 422 responses
|
|
96
|
+
*/
|
|
97
|
+
export interface IValidationError {
|
|
98
|
+
loc: (string | number)[];
|
|
99
|
+
msg: string;
|
|
100
|
+
type: string;
|
|
101
|
+
}
|
|
102
|
+
export declare class AIClientValidationError extends AIClientError {
|
|
103
|
+
validationErrors: IValidationError[];
|
|
104
|
+
constructor(validationErrors: IValidationError[]);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Common utility types used across AI clients
|
|
108
|
+
*/
|
|
109
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
110
|
+
/**
|
|
111
|
+
* Standard API response wrapper that many AI services use
|
|
112
|
+
*/
|
|
113
|
+
export interface IAPIResponse<T = unknown> {
|
|
114
|
+
data?: T;
|
|
115
|
+
error?: string;
|
|
116
|
+
status?: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Generic streaming handler interface for AI clients that support streaming
|
|
120
|
+
*/
|
|
121
|
+
export interface IStreamingHandler<TChunk = unknown> {
|
|
122
|
+
/**
|
|
123
|
+
* Handle a single chunk of streaming data
|
|
124
|
+
*/
|
|
125
|
+
onChunk(chunk: TChunk, afterChunk?: (chunk: TChunk) => void): void;
|
|
126
|
+
/**
|
|
127
|
+
* Called when the stream starts
|
|
128
|
+
*/
|
|
129
|
+
onStart?(conversationId?: string, messageId?: string): void;
|
|
130
|
+
/**
|
|
131
|
+
* Called when the stream completes successfully
|
|
132
|
+
*/
|
|
133
|
+
onComplete?(finalChunk: TChunk): void;
|
|
134
|
+
/**
|
|
135
|
+
* Called when an error occurs during streaming
|
|
136
|
+
*/
|
|
137
|
+
onError?(error: Error): void;
|
|
138
|
+
/**
|
|
139
|
+
* Called when the stream is aborted
|
|
140
|
+
*/
|
|
141
|
+
onAbort?(): void;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Streaming request options for clients that support streaming
|
|
145
|
+
*/
|
|
146
|
+
export interface IStreamingRequestOptions<TChunk = unknown> extends IRequestOptions {
|
|
147
|
+
/**
|
|
148
|
+
* The streaming handler to process the response
|
|
149
|
+
*/
|
|
150
|
+
streamingHandler: IStreamingHandler<TChunk>;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Standard message response for non-streaming requests
|
|
154
|
+
*/
|
|
155
|
+
export interface IMessageResponse<AP extends Record<string, unknown> = Record<string, unknown>> {
|
|
156
|
+
/**
|
|
157
|
+
* Unique identifier for the message
|
|
158
|
+
*/
|
|
159
|
+
messageId: string;
|
|
160
|
+
/**
|
|
161
|
+
* The response answer/text
|
|
162
|
+
*/
|
|
163
|
+
answer: string;
|
|
164
|
+
/**
|
|
165
|
+
* Conversation identifier this message belongs to
|
|
166
|
+
*/
|
|
167
|
+
conversationId: string;
|
|
168
|
+
/**
|
|
169
|
+
* Timestamp when the message was created
|
|
170
|
+
*/
|
|
171
|
+
createdAt?: string;
|
|
172
|
+
/**
|
|
173
|
+
* Additional attributes specific to the AI client
|
|
174
|
+
*/
|
|
175
|
+
additionalAttributes?: AP;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Source citation information for AI responses
|
|
179
|
+
*/
|
|
180
|
+
export interface IAnswerSource {
|
|
181
|
+
/**
|
|
182
|
+
* URL to the source content
|
|
183
|
+
*/
|
|
184
|
+
link?: string | null;
|
|
185
|
+
/**
|
|
186
|
+
* Title of the source content
|
|
187
|
+
*/
|
|
188
|
+
title?: string | null;
|
|
189
|
+
/**
|
|
190
|
+
* Relevance score of the source
|
|
191
|
+
*/
|
|
192
|
+
score?: number | null;
|
|
193
|
+
/**
|
|
194
|
+
* Text snippet from the source
|
|
195
|
+
*/
|
|
196
|
+
snippet?: string | null;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Single message in conversation history
|
|
200
|
+
*/
|
|
201
|
+
export interface IConversationHistoryMessage<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
202
|
+
/**
|
|
203
|
+
* Unique identifier for the message
|
|
204
|
+
*/
|
|
205
|
+
message_id: string;
|
|
206
|
+
/**
|
|
207
|
+
* When the message was received
|
|
208
|
+
*/
|
|
209
|
+
received_at: string;
|
|
210
|
+
/**
|
|
211
|
+
* The user's input/question
|
|
212
|
+
*/
|
|
213
|
+
input: string;
|
|
214
|
+
/**
|
|
215
|
+
* When the AI response was created
|
|
216
|
+
*/
|
|
217
|
+
created_at: string;
|
|
218
|
+
/**
|
|
219
|
+
* The AI's answer
|
|
220
|
+
*/
|
|
221
|
+
answer: string;
|
|
222
|
+
/**
|
|
223
|
+
* Sources used to generate the answer
|
|
224
|
+
*/
|
|
225
|
+
sources: IAnswerSource[];
|
|
226
|
+
/**
|
|
227
|
+
* Additional data associated with the message
|
|
228
|
+
*/
|
|
229
|
+
additionalData?: T;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Response type for conversation history requests
|
|
233
|
+
*/
|
|
234
|
+
export type IConversationHistoryResponse<T extends Record<string, unknown> = Record<string, unknown>> = IConversationHistoryMessage<T>[] | null;
|
|
235
|
+
export interface IErrorMessageResponse {
|
|
236
|
+
error: any;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Options for sending messages, supporting both streaming and non-streaming modes
|
|
240
|
+
*/
|
|
241
|
+
export interface ISendMessageOptions<TChunk = unknown> extends IRequestOptions {
|
|
242
|
+
/**
|
|
243
|
+
* Whether to use streaming mode for the response
|
|
244
|
+
* When true, the client's default streaming handler will be used
|
|
245
|
+
*/
|
|
246
|
+
stream?: boolean;
|
|
247
|
+
afterChunk?: (chunk: TChunk) => void;
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=ai-client-common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-client-common.d.ts","sourceRoot":"","sources":["../../../../../packages/ai-client-common/src/lib/ai-client-common.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AIClientValidationError = exports.AIClientError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base error class for all AI client errors
|
|
6
|
+
*/
|
|
7
|
+
class AIClientError extends Error {
|
|
8
|
+
constructor(status, statusText, message, data) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.statusText = statusText;
|
|
12
|
+
this.data = data;
|
|
13
|
+
this.name = 'AIClientError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.AIClientError = AIClientError;
|
|
17
|
+
class AIClientValidationError extends AIClientError {
|
|
18
|
+
constructor(validationErrors) {
|
|
19
|
+
super(422, 'Validation Error', 'Request validation failed', validationErrors);
|
|
20
|
+
this.validationErrors = validationErrors;
|
|
21
|
+
this.name = 'AIClientValidationError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.AIClientValidationError = AIClientValidationError;
|
|
25
|
+
//# sourceMappingURL=ai-client-common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-client-common.js","sourceRoot":"","sources":["../../../../../packages/ai-client-common/src/lib/ai-client-common.ts"],"names":[],"mappings":";;;AAuGA;;GAEG;AACH,MAAa,aAAc,SAAQ,KAAK;IACtC,YACS,MAAc,EACd,UAAkB,EACzB,OAAe,EACR,IAAc;QAErB,KAAK,CAAC,OAAO,CAAC,CAAC;QALR,WAAM,GAAN,MAAM,CAAQ;QACd,eAAU,GAAV,UAAU,CAAQ;QAElB,SAAI,GAAJ,IAAI,CAAU;QAGrB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAVD,sCAUC;AAWD,MAAa,uBAAwB,SAAQ,aAAa;IACxD,YAAmB,gBAAoC;QACrD,KAAK,CAAC,GAAG,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,gBAAgB,CAAC,CAAC;QAD7D,qBAAgB,GAAhB,gBAAgB,CAAoB;QAErD,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AALD,0DAKC"}
|
package/.eslintrc.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": ["../../.eslintrc.json"],
|
|
3
|
-
"ignorePatterns": ["!**/*"],
|
|
4
|
-
"overrides": [
|
|
5
|
-
{
|
|
6
|
-
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
-
"rules": {}
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"files": ["*.ts", "*.tsx"],
|
|
11
|
-
"rules": {}
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"files": ["*.js", "*.jsx"],
|
|
15
|
-
"rules": {}
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"files": ["*.json"],
|
|
19
|
-
"parser": "jsonc-eslint-parser",
|
|
20
|
-
"rules": {
|
|
21
|
-
"@nx/dependency-checks": [
|
|
22
|
-
"error",
|
|
23
|
-
{
|
|
24
|
-
"ignoredFiles": [
|
|
25
|
-
"{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}"
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
package/jest.config.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
displayName: 'ai-client-common',
|
|
3
|
-
preset: '../../jest.preset.js',
|
|
4
|
-
testEnvironment: 'node',
|
|
5
|
-
transform: {
|
|
6
|
-
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
|
7
|
-
},
|
|
8
|
-
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
9
|
-
coverageDirectory: '../../coverage/packages/ai-client-common',
|
|
10
|
-
};
|
package/project.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ai-client-common",
|
|
3
|
-
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/ai-client-common/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"release": {
|
|
7
|
-
"version": {
|
|
8
|
-
"manifestRootsToUpdate": ["dist/{projectRoot}"],
|
|
9
|
-
"currentVersionResolver": "git-tag",
|
|
10
|
-
"fallbackCurrentVersionResolver": "disk"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"tags": [],
|
|
14
|
-
"targets": {
|
|
15
|
-
"build": {
|
|
16
|
-
"executor": "@nx/js:tsc",
|
|
17
|
-
"outputs": ["{options.outputPath}"],
|
|
18
|
-
"options": {
|
|
19
|
-
"outputPath": "dist/packages/ai-client-common",
|
|
20
|
-
"main": "packages/ai-client-common/src/index.ts",
|
|
21
|
-
"tsConfig": "packages/ai-client-common/tsconfig.lib.json",
|
|
22
|
-
"assets": ["packages/ai-client-common/*.md"]
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"test": {
|
|
26
|
-
"executor": "@nx/jest:jest",
|
|
27
|
-
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
28
|
-
"options": {
|
|
29
|
-
"jestConfig": "packages/ai-client-common/jest.config.ts"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AIClientError,
|
|
3
|
-
AIClientValidationError,
|
|
4
|
-
IValidationError,
|
|
5
|
-
IBaseClientConfig,
|
|
6
|
-
IRequestOptions,
|
|
7
|
-
IAIClient,
|
|
8
|
-
IStreamingHandler,
|
|
9
|
-
IStreamingRequestOptions,
|
|
10
|
-
IAPIResponse,
|
|
11
|
-
IFetchFunction,
|
|
12
|
-
HttpMethod,
|
|
13
|
-
IMessageResponse,
|
|
14
|
-
ISendMessageOptions,
|
|
15
|
-
IConversationHistoryResponse,
|
|
16
|
-
} from './ai-client-common';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
type AdditionalAttributesTest = {
|
|
20
|
-
foo?: string;
|
|
21
|
-
bar?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('ai-client-common', () => {
|
|
25
|
-
describe('AIClientError', () => {
|
|
26
|
-
it('should create error with all properties', () => {
|
|
27
|
-
const error = new AIClientError(404, 'Not Found', 'Resource not found', { id: '123' });
|
|
28
|
-
|
|
29
|
-
expect(error.name).toBe('AIClientError');
|
|
30
|
-
expect(error.message).toBe('Resource not found');
|
|
31
|
-
expect(error.status).toBe(404);
|
|
32
|
-
expect(error.statusText).toBe('Not Found');
|
|
33
|
-
expect(error.data).toEqual({ id: '123' });
|
|
34
|
-
expect(error instanceof Error).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should create error without optional data', () => {
|
|
38
|
-
const error = new AIClientError(500, 'Internal Server Error', 'Something went wrong');
|
|
39
|
-
|
|
40
|
-
expect(error.name).toBe('AIClientError');
|
|
41
|
-
expect(error.message).toBe('Something went wrong');
|
|
42
|
-
expect(error.status).toBe(500);
|
|
43
|
-
expect(error.statusText).toBe('Internal Server Error');
|
|
44
|
-
expect(error.data).toBeUndefined();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should be instanceof Error and AIClientError', () => {
|
|
48
|
-
const error = new AIClientError(400, 'Bad Request', 'Invalid input');
|
|
49
|
-
|
|
50
|
-
expect(error instanceof Error).toBe(true);
|
|
51
|
-
expect(error instanceof AIClientError).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('AIClientValidationError', () => {
|
|
56
|
-
it('should create validation error with validation details', () => {
|
|
57
|
-
const validationErrors: IValidationError[] = [
|
|
58
|
-
{ loc: ['field1'], msg: 'Field is required', type: 'missing' },
|
|
59
|
-
{ loc: ['field2', 0], msg: 'Invalid type', type: 'type_error' }
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
const error = new AIClientValidationError(validationErrors);
|
|
63
|
-
|
|
64
|
-
expect(error.name).toBe('AIClientValidationError');
|
|
65
|
-
expect(error.message).toBe('Request validation failed');
|
|
66
|
-
expect(error.status).toBe(422);
|
|
67
|
-
expect(error.statusText).toBe('Validation Error');
|
|
68
|
-
expect(error.data).toEqual(validationErrors);
|
|
69
|
-
expect(error.validationErrors).toEqual(validationErrors);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should be instanceof Error, AIClientError, and AIClientValidationError', () => {
|
|
73
|
-
const validationErrors: IValidationError[] = [
|
|
74
|
-
{ loc: ['test'], msg: 'Test error', type: 'test' }
|
|
75
|
-
];
|
|
76
|
-
const error = new AIClientValidationError(validationErrors);
|
|
77
|
-
|
|
78
|
-
expect(error instanceof Error).toBe(true);
|
|
79
|
-
expect(error instanceof AIClientError).toBe(true);
|
|
80
|
-
expect(error instanceof AIClientValidationError).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should handle empty validation errors array', () => {
|
|
84
|
-
const error = new AIClientValidationError([]);
|
|
85
|
-
|
|
86
|
-
expect(error.validationErrors).toEqual([]);
|
|
87
|
-
expect(error.data).toEqual([]);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe('Type Definitions', () => {
|
|
92
|
-
it('should accept valid HttpMethod values', () => {
|
|
93
|
-
const methods: HttpMethod[] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
|
|
94
|
-
|
|
95
|
-
methods.forEach(method => {
|
|
96
|
-
expect(typeof method).toBe('string');
|
|
97
|
-
expect(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).toContain(method);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should validate IValidationError structure', () => {
|
|
102
|
-
const validationError: IValidationError = {
|
|
103
|
-
loc: ['field', 'subfield'],
|
|
104
|
-
msg: 'Error message',
|
|
105
|
-
type: 'validation_error'
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
expect(Array.isArray(validationError.loc)).toBe(true);
|
|
109
|
-
expect(typeof validationError.msg).toBe('string');
|
|
110
|
-
expect(typeof validationError.type).toBe('string');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should validate IAPIResponse structure', () => {
|
|
114
|
-
const response: IAPIResponse<{ test: string }> = {
|
|
115
|
-
data: { test: 'value' },
|
|
116
|
-
error: 'No error',
|
|
117
|
-
status: 200
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
expect(response.data).toEqual({ test: 'value' });
|
|
121
|
-
expect(response.error).toBe('No error');
|
|
122
|
-
expect(response.status).toBe(200);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should validate IBaseClientConfig structure without default handler', () => {
|
|
126
|
-
const mockFetch: IFetchFunction = async (input, init) => {
|
|
127
|
-
void input; void init; // Mark parameters as used for linting
|
|
128
|
-
return new Response('{}', { status: 200 });
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const config: IBaseClientConfig = {
|
|
132
|
-
baseUrl: 'https://api.example.com',
|
|
133
|
-
fetchFunction: mockFetch
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
expect(typeof config.baseUrl).toBe('string');
|
|
137
|
-
expect(typeof config.fetchFunction).toBe('function');
|
|
138
|
-
expect(config.defaultStreamingHandler).toBeUndefined();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should validate IBaseClientConfig structure with default handler', () => {
|
|
142
|
-
const mockFetch: IFetchFunction = async (input, init) => {
|
|
143
|
-
void input; void init; // Mark parameters as used for linting
|
|
144
|
-
return new Response('{}', { status: 200 });
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const mockHandler: IStreamingHandler<string> = {
|
|
148
|
-
onChunk: (chunk: string) => { void chunk; /* process */ }
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const config: IBaseClientConfig<string> = {
|
|
152
|
-
baseUrl: 'https://api.example.com',
|
|
153
|
-
fetchFunction: mockFetch,
|
|
154
|
-
defaultStreamingHandler: mockHandler
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
expect(typeof config.baseUrl).toBe('string');
|
|
158
|
-
expect(typeof config.fetchFunction).toBe('function');
|
|
159
|
-
expect(typeof config.defaultStreamingHandler).toBe('object');
|
|
160
|
-
expect(typeof config.defaultStreamingHandler?.onChunk).toBe('function');
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should validate IRequestOptions structure', () => {
|
|
164
|
-
const options: IRequestOptions = {
|
|
165
|
-
headers: { 'Authorization': 'Bearer token' },
|
|
166
|
-
signal: new AbortController().signal
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
expect(typeof options.headers).toBe('object');
|
|
170
|
-
expect(options.signal instanceof AbortSignal).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should validate IMessageResponse structure', () => {
|
|
174
|
-
const messageResponse: IMessageResponse = {
|
|
175
|
-
messageId: 'msg-123',
|
|
176
|
-
answer: 'Hello world',
|
|
177
|
-
conversationId: 'conv-456',
|
|
178
|
-
createdAt: '2024-01-01T10:00:00Z',
|
|
179
|
-
additionalAttributes: {
|
|
180
|
-
model: 'gpt-4',
|
|
181
|
-
tokens: 50
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
expect(typeof messageResponse.messageId).toBe('string');
|
|
186
|
-
expect(typeof messageResponse.answer).toBe('string');
|
|
187
|
-
expect(typeof messageResponse.conversationId).toBe('string');
|
|
188
|
-
expect(typeof messageResponse.createdAt).toBe('string');
|
|
189
|
-
expect(typeof messageResponse.additionalAttributes).toBe('object');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should validate ISendMessageOptions structure for non-streaming', () => {
|
|
193
|
-
const options: ISendMessageOptions = {
|
|
194
|
-
headers: { 'Content-Type': 'application/json' },
|
|
195
|
-
signal: new AbortController().signal,
|
|
196
|
-
stream: false
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
expect(typeof options.headers).toBe('object');
|
|
200
|
-
expect(options.signal instanceof AbortSignal).toBe(true);
|
|
201
|
-
expect(options.stream).toBe(false);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('should validate ISendMessageOptions structure for streaming', () => {
|
|
205
|
-
const options: ISendMessageOptions = {
|
|
206
|
-
headers: { 'Accept': 'text/stream' },
|
|
207
|
-
stream: true
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
expect(typeof options.headers).toBe('object');
|
|
211
|
-
expect(options.stream).toBe(true);
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('Interface Compliance', () => {
|
|
216
|
-
it('should allow proper IAIClient implementation', () => {
|
|
217
|
-
class TestClient implements IAIClient<Record<string, unknown>> {
|
|
218
|
-
async init(): Promise<string> {
|
|
219
|
-
return 'test-conversation-id';
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async sendMessage(
|
|
223
|
-
conversationId: string,
|
|
224
|
-
message: string,
|
|
225
|
-
options?: ISendMessageOptions
|
|
226
|
-
): Promise<IMessageResponse | void> {
|
|
227
|
-
void conversationId; void message; void options; // Mark parameters as used for linting
|
|
228
|
-
if (options?.stream) {
|
|
229
|
-
return; // Streaming mode returns void
|
|
230
|
-
}
|
|
231
|
-
return {
|
|
232
|
-
messageId: 'msg-123',
|
|
233
|
-
answer: 'Response content',
|
|
234
|
-
conversationId: 'conv-456'
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
getDefaultStreamingHandler<TChunk = unknown>(): IStreamingHandler<TChunk> | undefined {
|
|
239
|
-
return undefined; // No default handler for this test client
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
async getConversationHistory<T extends AdditionalAttributesTest>(
|
|
243
|
-
conversationId: string,
|
|
244
|
-
options?: IRequestOptions
|
|
245
|
-
): Promise<IConversationHistoryResponse<T>> {
|
|
246
|
-
void conversationId; void options; // Mark parameters as used for linting
|
|
247
|
-
return [];
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async healthCheck(options?: IRequestOptions): Promise<unknown> {
|
|
251
|
-
void options; // Mark parameter as used for linting
|
|
252
|
-
return { status: 'healthy' };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
async getServiceStatus(options?: IRequestOptions): Promise<unknown> {
|
|
256
|
-
void options; // Mark parameter as used for linting
|
|
257
|
-
return { status: 'running' };
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const client = new TestClient();
|
|
262
|
-
expect(typeof client.sendMessage).toBe('function');
|
|
263
|
-
expect(typeof client.healthCheck).toBe('function');
|
|
264
|
-
expect(typeof client.getServiceStatus).toBe('function');
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('should handle sendMessage in non-streaming mode', async () => {
|
|
268
|
-
class TestClient implements IAIClient {
|
|
269
|
-
async init(): Promise<string> {
|
|
270
|
-
return 'test-conversation-id';
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async sendMessage(
|
|
274
|
-
conversationId: string,
|
|
275
|
-
message: string,
|
|
276
|
-
options?: ISendMessageOptions
|
|
277
|
-
): Promise<IMessageResponse | void> {
|
|
278
|
-
void options; // Mark parameter as used for linting
|
|
279
|
-
return {
|
|
280
|
-
messageId: 'msg-123',
|
|
281
|
-
answer: `Response to: ${message}`,
|
|
282
|
-
conversationId
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
getDefaultStreamingHandler<TChunk = unknown>(): IStreamingHandler<TChunk> | undefined {
|
|
287
|
-
return undefined;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async getConversationHistory<T extends AdditionalAttributesTest>(
|
|
291
|
-
conversationId: string,
|
|
292
|
-
options?: IRequestOptions
|
|
293
|
-
): Promise<IConversationHistoryResponse<T>> {
|
|
294
|
-
void conversationId; void options; // Mark parameters as used for linting
|
|
295
|
-
return [];
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
async healthCheck(): Promise<unknown> {
|
|
299
|
-
return { status: 'healthy' };
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const client = new TestClient();
|
|
304
|
-
const response = await client.sendMessage('conv-123', 'Hello AI');
|
|
305
|
-
|
|
306
|
-
expect(response).toBeDefined();
|
|
307
|
-
if (response) {
|
|
308
|
-
expect(response.messageId).toBe('msg-123');
|
|
309
|
-
expect(response.answer).toBe('Response to: Hello AI');
|
|
310
|
-
expect(response.conversationId).toBe('conv-123');
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it('should handle sendMessage in streaming mode', async () => {
|
|
315
|
-
let chunks: string[] = [];
|
|
316
|
-
|
|
317
|
-
const mockHandler: IStreamingHandler<string> = {
|
|
318
|
-
onChunk: (chunk: string) => { chunks.push(chunk); },
|
|
319
|
-
onStart: () => { chunks = []; },
|
|
320
|
-
onComplete: () => { /* complete */ }
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
class TestClient implements IAIClient {
|
|
324
|
-
private defaultHandler: IStreamingHandler<string> = mockHandler;
|
|
325
|
-
|
|
326
|
-
async init(): Promise<string> {
|
|
327
|
-
return 'test-conversation-id';
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async sendMessage(
|
|
331
|
-
conversationId: string,
|
|
332
|
-
message: string,
|
|
333
|
-
options?: ISendMessageOptions
|
|
334
|
-
): Promise<IMessageResponse | void> {
|
|
335
|
-
if (options?.stream && this.defaultHandler) {
|
|
336
|
-
// Simulate streaming response using default handler
|
|
337
|
-
this.defaultHandler.onStart?.(conversationId, 'msg-456');
|
|
338
|
-
this.defaultHandler.onChunk('Hello');
|
|
339
|
-
this.defaultHandler.onChunk(' world');
|
|
340
|
-
this.defaultHandler.onComplete?.('Hello world');
|
|
341
|
-
return; // Streaming returns void
|
|
342
|
-
}
|
|
343
|
-
return {
|
|
344
|
-
messageId: 'msg-456',
|
|
345
|
-
answer: 'Non-streaming response',
|
|
346
|
-
conversationId
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
getDefaultStreamingHandler<TChunk = unknown>(): IStreamingHandler<TChunk> | undefined {
|
|
351
|
-
return this.defaultHandler as IStreamingHandler<TChunk>;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
async getConversationHistory<T extends AdditionalAttributesTest>(
|
|
355
|
-
conversationId: string,
|
|
356
|
-
options?: IRequestOptions
|
|
357
|
-
): Promise<IConversationHistoryResponse<T>> {
|
|
358
|
-
void conversationId; void options; // Mark parameters as used for linting
|
|
359
|
-
return [];
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
async healthCheck(): Promise<unknown> {
|
|
363
|
-
return { status: 'healthy' };
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const client = new TestClient();
|
|
368
|
-
|
|
369
|
-
const response = await client.sendMessage('conv-123', 'Hello AI', {
|
|
370
|
-
stream: true
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
expect(response).toBeUndefined(); // Streaming returns void
|
|
374
|
-
expect(chunks).toEqual(['Hello', ' world']);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// TODO: Add integration tests for default streaming handler functionality
|
|
378
|
-
// Complex TypeScript generics make testing challenging - will be covered in real implementations
|
|
379
|
-
|
|
380
|
-
it('should allow proper IStreamingHandler implementation', () => {
|
|
381
|
-
class TestStreamingHandler implements IStreamingHandler<string> {
|
|
382
|
-
onChunk(chunk: string): void {
|
|
383
|
-
void chunk; // Mark parameter as used for linting
|
|
384
|
-
// Process chunk
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
onStart(conversationId?: string, messageId?: string): void {
|
|
388
|
-
void conversationId; void messageId; // Mark parameters as used for linting
|
|
389
|
-
// Handle start
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
onComplete(finalChunk: string): void {
|
|
393
|
-
void finalChunk; // Mark parameter as used for linting
|
|
394
|
-
// Handle completion
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
onError(error: Error): void {
|
|
398
|
-
void error; // Mark parameter as used for linting
|
|
399
|
-
// Handle error
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
onAbort(): void {
|
|
403
|
-
// Handle abort
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const handler = new TestStreamingHandler();
|
|
408
|
-
expect(typeof handler.onChunk).toBe('function');
|
|
409
|
-
expect(typeof handler.onStart).toBe('function');
|
|
410
|
-
expect(typeof handler.onComplete).toBe('function');
|
|
411
|
-
expect(typeof handler.onError).toBe('function');
|
|
412
|
-
expect(typeof handler.onAbort).toBe('function');
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
it('should validate IStreamingRequestOptions structure', () => {
|
|
416
|
-
const mockHandler: IStreamingHandler<string> = {
|
|
417
|
-
onChunk: (chunk: string) => { void chunk; /* process */ }
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
const options: IStreamingRequestOptions<string> = {
|
|
421
|
-
headers: { 'Accept': 'text/stream' },
|
|
422
|
-
signal: new AbortController().signal,
|
|
423
|
-
streamingHandler: mockHandler
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
expect(typeof options.headers).toBe('object');
|
|
427
|
-
expect(options.signal instanceof AbortSignal).toBe(true);
|
|
428
|
-
expect(typeof options.streamingHandler).toBe('object');
|
|
429
|
-
expect(typeof options.streamingHandler.onChunk).toBe('function');
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
describe('IFetchFunction Interface', () => {
|
|
434
|
-
it('should match native fetch signature', async () => {
|
|
435
|
-
const mockFetch: IFetchFunction = async (input, init) => {
|
|
436
|
-
void input; void init; // Mark parameters as used for linting
|
|
437
|
-
return new Response('{"success": true}', {
|
|
438
|
-
status: 200,
|
|
439
|
-
headers: { 'Content-Type': 'application/json' }
|
|
440
|
-
});
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
// Test with URL string
|
|
444
|
-
const response1 = await mockFetch('https://api.example.com/test');
|
|
445
|
-
expect(response1.status).toBe(200);
|
|
446
|
-
|
|
447
|
-
// Test with Request object
|
|
448
|
-
const request = new Request('https://api.example.com/test', { method: 'POST' });
|
|
449
|
-
const response2 = await mockFetch(request);
|
|
450
|
-
expect(response2.status).toBe(200);
|
|
451
|
-
|
|
452
|
-
// Test with RequestInit options
|
|
453
|
-
const response3 = await mockFetch('https://api.example.com/test', {
|
|
454
|
-
method: 'PUT',
|
|
455
|
-
headers: { 'Content-Type': 'application/json' }
|
|
456
|
-
});
|
|
457
|
-
expect(response3.status).toBe(200);
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
});
|
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic interface for custom fetch implementation that must be injected into AI clients.
|
|
3
|
-
* Mirrors exactly the native browser fetch interface for maximum compatibility.
|
|
4
|
-
*/
|
|
5
|
-
export interface IFetchFunction {
|
|
6
|
-
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Base configuration interface for all AI clients
|
|
11
|
-
*/
|
|
12
|
-
export interface IBaseClientConfig<TChunk = unknown> {
|
|
13
|
-
/**
|
|
14
|
-
* The base URL for the API
|
|
15
|
-
*/
|
|
16
|
-
baseUrl: string;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Custom fetch implementation for making HTTP requests
|
|
20
|
-
* Must include authentication headers (Bearer token) if needed
|
|
21
|
-
*/
|
|
22
|
-
fetchFunction: IFetchFunction;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Default streaming handler for the client
|
|
26
|
-
* Used when sendMessage is called with stream=true but no streamingHandler provided
|
|
27
|
-
* Individual requests can override this by providing their own streamingHandler
|
|
28
|
-
*/
|
|
29
|
-
defaultStreamingHandler?: IStreamingHandler<TChunk>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Standard request options that can be passed to individual client methods
|
|
34
|
-
*/
|
|
35
|
-
export interface IRequestOptions {
|
|
36
|
-
/**
|
|
37
|
-
* Custom headers for this specific request
|
|
38
|
-
*/
|
|
39
|
-
headers?: Record<string, string>;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* AbortSignal to cancel the request
|
|
43
|
-
*/
|
|
44
|
-
signal?: AbortSignal;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Common interface that all AI clients must implement
|
|
49
|
-
* Provides a standardized way to interact with different AI services
|
|
50
|
-
*/
|
|
51
|
-
export interface IAIClient<AP extends Record<string, unknown> = Record<string, unknown>> {
|
|
52
|
-
/**
|
|
53
|
-
* Initialize the client and return the initial conversation ID
|
|
54
|
-
* This method is called once when the client is first used by a state manager
|
|
55
|
-
* @returns Promise that resolves to the initial conversation ID
|
|
56
|
-
*/
|
|
57
|
-
init(): Promise<string>;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Send a message to the AI service
|
|
61
|
-
* @param conversationId - The conversation ID to send the message to
|
|
62
|
-
* @param message - The message content to send
|
|
63
|
-
* @param options - Optional configuration for the request
|
|
64
|
-
* @returns Promise that resolves to the AI's response
|
|
65
|
-
*/
|
|
66
|
-
sendMessage<TChunk = unknown>(
|
|
67
|
-
conversationId: string,
|
|
68
|
-
message: string,
|
|
69
|
-
options?: ISendMessageOptions<TChunk>
|
|
70
|
-
): Promise<TChunk | IMessageResponse<AP> | void>;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Get the default streaming handler for this client (if any)
|
|
74
|
-
* @returns The default streaming handler or undefined if not configured
|
|
75
|
-
*/
|
|
76
|
-
getDefaultStreamingHandler?<TChunk = unknown>(): IStreamingHandler<TChunk> | undefined;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Get the conversation history for a specific conversation
|
|
80
|
-
* @param conversationId - The conversation ID to retrieve history for
|
|
81
|
-
* @param options - Optional request configuration
|
|
82
|
-
* @returns Promise that resolves to the conversation history
|
|
83
|
-
*/
|
|
84
|
-
getConversationHistory(
|
|
85
|
-
conversationId: string,
|
|
86
|
-
options?: IRequestOptions
|
|
87
|
-
): Promise<IConversationHistoryResponse<AP>>;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Perform a health check on the AI service
|
|
91
|
-
* @param options - Optional request configuration
|
|
92
|
-
* @returns Promise that resolves to health status information
|
|
93
|
-
*/
|
|
94
|
-
healthCheck(options?: IRequestOptions): Promise<unknown>;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Get the current status of the AI service
|
|
98
|
-
* @param options - Optional request configuration
|
|
99
|
-
* @returns Promise that resolves to service status information
|
|
100
|
-
*/
|
|
101
|
-
getServiceStatus?(options?: IRequestOptions): Promise<unknown>;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Base error class for all AI client errors
|
|
106
|
-
*/
|
|
107
|
-
export class AIClientError extends Error {
|
|
108
|
-
constructor(
|
|
109
|
-
public status: number,
|
|
110
|
-
public statusText: string,
|
|
111
|
-
message: string,
|
|
112
|
-
public data?: unknown
|
|
113
|
-
) {
|
|
114
|
-
super(message);
|
|
115
|
-
this.name = 'AIClientError';
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Validation error class for 422 responses
|
|
121
|
-
*/
|
|
122
|
-
export interface IValidationError {
|
|
123
|
-
loc: (string | number)[];
|
|
124
|
-
msg: string;
|
|
125
|
-
type: string;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export class AIClientValidationError extends AIClientError {
|
|
129
|
-
constructor(public validationErrors: IValidationError[]) {
|
|
130
|
-
super(422, 'Validation Error', 'Request validation failed', validationErrors);
|
|
131
|
-
this.name = 'AIClientValidationError';
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Common utility types used across AI clients
|
|
137
|
-
*/
|
|
138
|
-
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Standard API response wrapper that many AI services use
|
|
142
|
-
*/
|
|
143
|
-
export interface IAPIResponse<T = unknown> {
|
|
144
|
-
data?: T;
|
|
145
|
-
error?: string;
|
|
146
|
-
status?: number;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Generic streaming handler interface for AI clients that support streaming
|
|
151
|
-
*/
|
|
152
|
-
export interface IStreamingHandler<TChunk = unknown> {
|
|
153
|
-
/**
|
|
154
|
-
* Handle a single chunk of streaming data
|
|
155
|
-
*/
|
|
156
|
-
onChunk(chunk: TChunk, afterChunk?: (chunk: TChunk) => void): void;
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Called when the stream starts
|
|
160
|
-
*/
|
|
161
|
-
onStart?(conversationId?: string, messageId?: string): void;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Called when the stream completes successfully
|
|
165
|
-
*/
|
|
166
|
-
onComplete?(finalChunk: TChunk): void;
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Called when an error occurs during streaming
|
|
170
|
-
*/
|
|
171
|
-
onError?(error: Error): void;
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Called when the stream is aborted
|
|
175
|
-
*/
|
|
176
|
-
onAbort?(): void;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Streaming request options for clients that support streaming
|
|
181
|
-
*/
|
|
182
|
-
export interface IStreamingRequestOptions<TChunk = unknown> extends IRequestOptions {
|
|
183
|
-
/**
|
|
184
|
-
* The streaming handler to process the response
|
|
185
|
-
*/
|
|
186
|
-
streamingHandler: IStreamingHandler<TChunk>;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Standard message response for non-streaming requests
|
|
191
|
-
*/
|
|
192
|
-
export interface IMessageResponse<AP extends Record<string, unknown> = Record<string, unknown>> {
|
|
193
|
-
/**
|
|
194
|
-
* Unique identifier for the message
|
|
195
|
-
*/
|
|
196
|
-
messageId: string;
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* The response answer/text
|
|
200
|
-
*/
|
|
201
|
-
answer: string;
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Conversation identifier this message belongs to
|
|
205
|
-
*/
|
|
206
|
-
conversationId: string;
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Timestamp when the message was created
|
|
210
|
-
*/
|
|
211
|
-
createdAt?: string;
|
|
212
|
-
/**
|
|
213
|
-
* Additional attributes specific to the AI client
|
|
214
|
-
*/
|
|
215
|
-
additionalAttributes?: AP;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Source citation information for AI responses
|
|
220
|
-
*/
|
|
221
|
-
export interface IAnswerSource {
|
|
222
|
-
/**
|
|
223
|
-
* URL to the source content
|
|
224
|
-
*/
|
|
225
|
-
link?: string | null;
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Title of the source content
|
|
229
|
-
*/
|
|
230
|
-
title?: string | null;
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Relevance score of the source
|
|
234
|
-
*/
|
|
235
|
-
score?: number | null;
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Text snippet from the source
|
|
239
|
-
*/
|
|
240
|
-
snippet?: string | null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Single message in conversation history
|
|
245
|
-
*/
|
|
246
|
-
export interface IConversationHistoryMessage<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
247
|
-
/**
|
|
248
|
-
* Unique identifier for the message
|
|
249
|
-
*/
|
|
250
|
-
message_id: string;
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* When the message was received
|
|
254
|
-
*/
|
|
255
|
-
received_at: string;
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* The user's input/question
|
|
259
|
-
*/
|
|
260
|
-
input: string;
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* When the AI response was created
|
|
264
|
-
*/
|
|
265
|
-
created_at: string;
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* The AI's answer
|
|
269
|
-
*/
|
|
270
|
-
answer: string;
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Sources used to generate the answer
|
|
274
|
-
*/
|
|
275
|
-
sources: IAnswerSource[];
|
|
276
|
-
/**
|
|
277
|
-
* Additional data associated with the message
|
|
278
|
-
*/
|
|
279
|
-
additionalData?: T;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Response type for conversation history requests
|
|
284
|
-
*/
|
|
285
|
-
export type IConversationHistoryResponse<T extends Record<string, unknown> = Record<string, unknown>> = IConversationHistoryMessage<T>[] | null;
|
|
286
|
-
|
|
287
|
-
export interface IErrorMessageResponse {
|
|
288
|
-
error: any;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Options for sending messages, supporting both streaming and non-streaming modes
|
|
293
|
-
*/
|
|
294
|
-
export interface ISendMessageOptions<TChunk = unknown> extends IRequestOptions {
|
|
295
|
-
/**
|
|
296
|
-
* Whether to use streaming mode for the response
|
|
297
|
-
* When true, the client's default streaming handler will be used
|
|
298
|
-
*/
|
|
299
|
-
stream?: boolean;
|
|
300
|
-
afterChunk?: (chunk: TChunk) => void;
|
|
301
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"forceConsistentCasingInFileNames": true,
|
|
6
|
-
"strict": true,
|
|
7
|
-
"importHelpers": false,
|
|
8
|
-
"noImplicitOverride": true,
|
|
9
|
-
"noImplicitReturns": true,
|
|
10
|
-
"noFallthroughCasesInSwitch": true,
|
|
11
|
-
"noPropertyAccessFromIndexSignature": true
|
|
12
|
-
},
|
|
13
|
-
"files": [],
|
|
14
|
-
"include": [],
|
|
15
|
-
"references": [
|
|
16
|
-
{
|
|
17
|
-
"path": "./tsconfig.lib.json"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"path": "./tsconfig.spec.json"
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
package/tsconfig.lib.json
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../dist/out-tsc",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"declarationMap": true,
|
|
7
|
-
"module": "commonjs",
|
|
8
|
-
"types": ["node"]
|
|
9
|
-
},
|
|
10
|
-
"include": ["src/**/*.ts"],
|
|
11
|
-
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
|
12
|
-
}
|
package/tsconfig.spec.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../dist/out-tsc",
|
|
5
|
-
"module": "commonjs",
|
|
6
|
-
"moduleResolution": "node10",
|
|
7
|
-
"types": ["jest", "node"]
|
|
8
|
-
},
|
|
9
|
-
"include": [
|
|
10
|
-
"jest.config.ts",
|
|
11
|
-
"src/**/*.test.ts",
|
|
12
|
-
"src/**/*.spec.ts",
|
|
13
|
-
"src/**/*.d.ts"
|
|
14
|
-
]
|
|
15
|
-
}
|