@ragbits/api-client 0.0.1
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/README.md +146 -0
- package/eslint.config.js +21 -0
- package/package.json +48 -0
- package/prettier.config.js +6 -0
- package/src/index.ts +213 -0
- package/src/test/RagbitsClient.test.ts +484 -0
- package/src/test/mocks/handlers.ts +98 -0
- package/src/test/setup.ts +23 -0
- package/src/test/types.test.ts +35 -0
- package/src/test/utils.ts +28 -0
- package/src/types.ts +259 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Ragbits API Client
|
|
2
|
+
|
|
3
|
+
A TypeScript client for communicating with the Ragbits API. This client provides methods for both regular HTTP requests and server-sent events (streaming) functionality.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install ragbits-api-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { RagbitsClient, type ChatResponse } from 'ragbits-api-client'
|
|
15
|
+
|
|
16
|
+
// Initialize the client
|
|
17
|
+
const client = new RagbitsClient({
|
|
18
|
+
baseUrl: 'http://127.0.0.1:8000', // Optional, defaults to http://127.0.0.1:8000
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Get API configuration
|
|
22
|
+
const config = await client.getConfig()
|
|
23
|
+
|
|
24
|
+
// Send a chat message with streaming response
|
|
25
|
+
const cleanup = client.sendChatMessage(
|
|
26
|
+
{
|
|
27
|
+
message: 'Hello!',
|
|
28
|
+
history: [],
|
|
29
|
+
context: {}, // Optional
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
onMessage: (data: ChatResponse) => {
|
|
33
|
+
console.log('Received message:', data)
|
|
34
|
+
},
|
|
35
|
+
onError: (error: string) => {
|
|
36
|
+
console.error('Error:', error)
|
|
37
|
+
},
|
|
38
|
+
onClose: () => {
|
|
39
|
+
console.log('Stream closed')
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// Cancel the stream if needed
|
|
45
|
+
cleanup()
|
|
46
|
+
|
|
47
|
+
// Send feedback
|
|
48
|
+
await client.sendFeedback({
|
|
49
|
+
message_id: 'message-123',
|
|
50
|
+
feedback: 'like',
|
|
51
|
+
payload: { reason: 'helpful' },
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## API Reference
|
|
56
|
+
|
|
57
|
+
### `RagbitsClient`
|
|
58
|
+
|
|
59
|
+
The main client class for interacting with the Ragbits API.
|
|
60
|
+
|
|
61
|
+
#### Constructor
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
new RagbitsClient(config?: { baseUrl?: string })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- `config.baseUrl` (optional): Base URL for the API. Defaults to 'http://127.0.0.1:8000'
|
|
68
|
+
|
|
69
|
+
#### Methods
|
|
70
|
+
|
|
71
|
+
##### `getConfig()`
|
|
72
|
+
|
|
73
|
+
Get the API configuration.
|
|
74
|
+
|
|
75
|
+
- Returns: `Promise<Record<string, unknown>>` - API configuration object
|
|
76
|
+
|
|
77
|
+
##### `sendChatMessage(chatRequest, callbacks)`
|
|
78
|
+
|
|
79
|
+
Send a chat message and receive streaming responses.
|
|
80
|
+
|
|
81
|
+
- Parameters:
|
|
82
|
+
- `chatRequest`: ChatRequest
|
|
83
|
+
- `message`: string - User message
|
|
84
|
+
- `history`: Array<{ role: string; content: string; id?: string }> - Chat history
|
|
85
|
+
- `context`: Record<string, unknown> (optional) - Additional context
|
|
86
|
+
- `callbacks`: StreamCallbacks
|
|
87
|
+
- `onMessage`: (data: ChatResponse) => void | Promise<void> - Called when a message chunk is received
|
|
88
|
+
- `onError`: (error: string) => void | Promise<void> - Called when an error occurs
|
|
89
|
+
- `onClose`: () => void | Promise<void> (optional) - Called when the stream closes
|
|
90
|
+
- Returns: `() => void` - Cleanup function to cancel the stream
|
|
91
|
+
|
|
92
|
+
##### `sendFeedback(feedbackData)`
|
|
93
|
+
|
|
94
|
+
Send feedback for a message.
|
|
95
|
+
|
|
96
|
+
- Parameters:
|
|
97
|
+
- `feedbackData`: FeedbackRequest
|
|
98
|
+
- `message_id`: string - ID of the message to provide feedback for
|
|
99
|
+
- `feedback`: string - Type of feedback
|
|
100
|
+
- `payload`: Record<string, unknown> | null - Additional feedback data
|
|
101
|
+
- Returns: `Promise<Record<string, unknown>>` - Feedback submission response
|
|
102
|
+
|
|
103
|
+
## Types
|
|
104
|
+
|
|
105
|
+
### ChatResponse
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
{
|
|
109
|
+
type: 'message' | 'reference' | 'state_update' | 'text' | 'message_id'
|
|
110
|
+
content: any
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ChatRequest
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
{
|
|
118
|
+
message: string;
|
|
119
|
+
history: Array<{
|
|
120
|
+
role: string;
|
|
121
|
+
content: string;
|
|
122
|
+
id?: string;
|
|
123
|
+
}>;
|
|
124
|
+
context?: Record<string, unknown>;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### FeedbackRequest
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
{
|
|
132
|
+
message_id: string
|
|
133
|
+
feedback: string
|
|
134
|
+
payload: Record<string, unknown> | null
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### StreamCallbacks
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
{
|
|
142
|
+
onMessage: (data: ChatResponse) => void | Promise<void>;
|
|
143
|
+
onError: (error: string) => void | Promise<void>;
|
|
144
|
+
onClose?: () => void | Promise<void>;
|
|
145
|
+
}
|
|
146
|
+
```
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import tseslint from 'typescript-eslint'
|
|
4
|
+
|
|
5
|
+
export default tseslint.config(
|
|
6
|
+
{ ignores: ['dist', 'coverage'] },
|
|
7
|
+
{
|
|
8
|
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
9
|
+
files: ['**/*.{ts,tsx}'],
|
|
10
|
+
languageOptions: {
|
|
11
|
+
ecmaVersion: 2020,
|
|
12
|
+
globals: globals.node,
|
|
13
|
+
},
|
|
14
|
+
rules: {
|
|
15
|
+
'@typescript-eslint/no-unused-vars': [
|
|
16
|
+
'error',
|
|
17
|
+
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
)
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ragbits/api-client",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "JavaScript client for the Ragbits API",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
18
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
19
|
+
"test": "vitest",
|
|
20
|
+
"test:run": "vitest run",
|
|
21
|
+
"test:coverage": "vitest run --coverage",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"format": "prettier --write .",
|
|
24
|
+
"format:check": "prettier --check ."
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"ragbits",
|
|
28
|
+
"api",
|
|
29
|
+
"client"
|
|
30
|
+
],
|
|
31
|
+
"author": "deepsense.ai",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@eslint/js": "^9.17.0",
|
|
35
|
+
"@rjsf/utils": "^5.24.12",
|
|
36
|
+
"@testing-library/jest-dom": "^6.4.0",
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
39
|
+
"eslint": "^9.17.0",
|
|
40
|
+
"globals": "^15.14.0",
|
|
41
|
+
"msw": "^2.0.0",
|
|
42
|
+
"prettier": "^3.4.2",
|
|
43
|
+
"tsup": "^8.0.0",
|
|
44
|
+
"typescript": "^5.0.0",
|
|
45
|
+
"typescript-eslint": "^8.18.2",
|
|
46
|
+
"vitest": "^1.6.0"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClientConfig,
|
|
3
|
+
StreamCallbacks,
|
|
4
|
+
ApiEndpointPath,
|
|
5
|
+
ApiEndpointResponse,
|
|
6
|
+
TypedApiRequestOptions,
|
|
7
|
+
StreamingEndpointPath,
|
|
8
|
+
StreamingEndpointRequest,
|
|
9
|
+
StreamingEndpointStream,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Client for communicating with the Ragbits API
|
|
14
|
+
*/
|
|
15
|
+
export class RagbitsClient {
|
|
16
|
+
private readonly baseUrl: string
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param config - Configuration object
|
|
20
|
+
*/
|
|
21
|
+
constructor(config: ClientConfig = {}) {
|
|
22
|
+
this.baseUrl = config.baseUrl || 'http://127.0.0.1:8000'
|
|
23
|
+
|
|
24
|
+
// Validate the base URL
|
|
25
|
+
try {
|
|
26
|
+
new URL(this.baseUrl)
|
|
27
|
+
} catch {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Invalid base URL: ${this.baseUrl}. Please provide a valid URL.`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (this.baseUrl.endsWith('/')) {
|
|
34
|
+
this.baseUrl = this.baseUrl.slice(0, -1)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the base URL used by this client
|
|
40
|
+
*/
|
|
41
|
+
getBaseUrl(): string {
|
|
42
|
+
return this.baseUrl
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build full API URL from path
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
private _buildApiUrl(path: string): string {
|
|
50
|
+
return `${this.baseUrl}${path}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Make a request to the API
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
private async _makeRequest(
|
|
58
|
+
url: string,
|
|
59
|
+
options: RequestInit = {}
|
|
60
|
+
): Promise<Response> {
|
|
61
|
+
const defaultOptions: RequestInit = {
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const response = await fetch(url, { ...defaultOptions, ...options })
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`HTTP error! status: ${response.status}`)
|
|
70
|
+
}
|
|
71
|
+
return response
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Method to make API requests to known endpoints only
|
|
76
|
+
* @param endpoint - API endpoint path (must be predefined)
|
|
77
|
+
* @param options - Typed request options for the specific endpoint
|
|
78
|
+
*/
|
|
79
|
+
async makeRequest<T extends ApiEndpointPath>(
|
|
80
|
+
endpoint: T,
|
|
81
|
+
options?: TypedApiRequestOptions<T>
|
|
82
|
+
): Promise<ApiEndpointResponse<T>> {
|
|
83
|
+
const {
|
|
84
|
+
method = 'GET',
|
|
85
|
+
body,
|
|
86
|
+
headers = {},
|
|
87
|
+
...restOptions
|
|
88
|
+
} = options || {}
|
|
89
|
+
|
|
90
|
+
const requestOptions: RequestInit = {
|
|
91
|
+
method,
|
|
92
|
+
headers,
|
|
93
|
+
...restOptions, // This will include signal and other fetch options
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (body && method !== 'GET') {
|
|
97
|
+
requestOptions.body =
|
|
98
|
+
typeof body === 'string' ? body : JSON.stringify(body)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const response = await this._makeRequest(
|
|
102
|
+
this._buildApiUrl(endpoint),
|
|
103
|
+
requestOptions
|
|
104
|
+
)
|
|
105
|
+
return response.json()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Method for streaming requests to known endpoints only
|
|
110
|
+
* @param endpoint - Streaming endpoint path (must be predefined)
|
|
111
|
+
* @param data - Request data
|
|
112
|
+
* @param callbacks - Stream callbacks
|
|
113
|
+
* @param signal - Optional AbortSignal for cancelling the request
|
|
114
|
+
*/
|
|
115
|
+
makeStreamRequest<T extends StreamingEndpointPath>(
|
|
116
|
+
endpoint: T,
|
|
117
|
+
data: StreamingEndpointRequest<T>,
|
|
118
|
+
callbacks: StreamCallbacks<StreamingEndpointStream<T>>,
|
|
119
|
+
signal?: AbortSignal
|
|
120
|
+
): () => void {
|
|
121
|
+
let isCancelled = false
|
|
122
|
+
|
|
123
|
+
const processStream = async (response: Response): Promise<void> => {
|
|
124
|
+
const reader = response.body
|
|
125
|
+
?.pipeThrough(new TextDecoderStream())
|
|
126
|
+
.getReader()
|
|
127
|
+
|
|
128
|
+
if (!reader) {
|
|
129
|
+
throw new Error('Response body is null')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
while (!isCancelled && !signal?.aborted) {
|
|
133
|
+
try {
|
|
134
|
+
const { value, done } = await reader.read()
|
|
135
|
+
if (done) {
|
|
136
|
+
callbacks.onClose?.()
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const lines = value.split('\n')
|
|
141
|
+
for (const line of lines) {
|
|
142
|
+
if (!line.startsWith('data: ')) continue
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const jsonString = line.replace('data: ', '').trim()
|
|
146
|
+
const parsedData = JSON.parse(
|
|
147
|
+
jsonString
|
|
148
|
+
) as StreamingEndpointStream<T>
|
|
149
|
+
await callbacks.onMessage(parsedData)
|
|
150
|
+
} catch (parseError) {
|
|
151
|
+
console.error('Error parsing JSON:', parseError)
|
|
152
|
+
await callbacks.onError(
|
|
153
|
+
new Error('Error processing server response')
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} catch (streamError) {
|
|
158
|
+
console.error('Stream error:', streamError)
|
|
159
|
+
await callbacks.onError(new Error('Error reading stream'))
|
|
160
|
+
break
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const startStream = async (): Promise<void> => {
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetch(this._buildApiUrl(endpoint), {
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers: {
|
|
170
|
+
'Content-Type': 'application/json',
|
|
171
|
+
Accept: 'text/event-stream',
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify(data),
|
|
174
|
+
signal,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
throw new Error(`HTTP error! status: ${response.status}`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
await processStream(response)
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (signal?.aborted) {
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.error('Request error:', error)
|
|
188
|
+
const errorMessage =
|
|
189
|
+
error instanceof Error
|
|
190
|
+
? error.message
|
|
191
|
+
: 'Error connecting to server'
|
|
192
|
+
await callbacks.onError(new Error(errorMessage))
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
startStream()
|
|
198
|
+
} catch (error) {
|
|
199
|
+
const errorMessage =
|
|
200
|
+
error instanceof Error
|
|
201
|
+
? error.message
|
|
202
|
+
: 'Failed to start stream'
|
|
203
|
+
callbacks.onError(new Error(errorMessage))
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return () => {
|
|
207
|
+
isCancelled = true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Re-export types
|
|
213
|
+
export * from './types'
|