@rethink-ai/core 0.1.5
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/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.js +154 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.js +2 -0
- package/package.json +24 -0
- package/src/client.ts +175 -0
- package/src/index.ts +6 -0
- package/src/types.ts +58 -0
- package/tsconfig.json +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 R-Universe Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# ReThink Core SDK
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@rethink-ai/core)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://core.rethink.web.id)
|
|
9
|
+
|
|
10
|
+
**Official SDK untuk mengakses ReThink Core API dengan mudah dan efisien**
|
|
11
|
+
|
|
12
|
+
[Bahasa Indonesia](#dokumentasi-bahasa-indonesia) | [English Documentation](#english-documentation)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Dokumentasi Bahasa Indonesia
|
|
19
|
+
|
|
20
|
+
**@rethink-ai/core** adalah library client resmi yang dirancang untuk mempermudah integrasi dengan layanan AI dari ReThink Core. Mendukung text generation (streaming & non-streaming) serta image generation
|
|
21
|
+
|
|
22
|
+
### 📦 Instalasi
|
|
23
|
+
|
|
24
|
+
Install package menggunakan npm, yarn, atau pnpm:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @rethink-ai/core
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 🚀 Penggunaan Dasar
|
|
31
|
+
|
|
32
|
+
#### 1. Inisialisasi Client
|
|
33
|
+
|
|
34
|
+
Import dan inisialisasi client. API Key bersifat opsional untuk tier gratis (dengan batasan tertentu)
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { rethink } from '@rethink-ai/core';
|
|
38
|
+
|
|
39
|
+
const client = new rethink({
|
|
40
|
+
apiKey: 'YOUR_API_KEY_HERE'
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### 2. Generate Teks (Chat Completion)
|
|
45
|
+
|
|
46
|
+
Mendapatkan respons teks sederhana dari model AI
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
async function contohGenerateTeks() {
|
|
50
|
+
try {
|
|
51
|
+
const response = await client.text.generate({
|
|
52
|
+
model: 'rethink-v1', // atau 'mistral', 'gpt-oss-120b', dll.
|
|
53
|
+
prompt: 'Jelaskan apa itu nebula'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log('AI:', response.content);
|
|
57
|
+
// Output: Nebula adalah...
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Terjadi kesalahan:', error.message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### 3. Streaming Teks (Real-time)
|
|
65
|
+
|
|
66
|
+
Mendapatkan respons secara bertahap (per kata/chunk) seperti efek mengetik. Sangat disarankan untuk pengalaman pengguna yang lebih baik
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
async function contohStreaming() {
|
|
70
|
+
try {
|
|
71
|
+
const stream = await client.text.generate({
|
|
72
|
+
model: 'rethink-v1',
|
|
73
|
+
prompt: 'Buatkan puisi pendek tentang hujan.',
|
|
74
|
+
stream: true // Aktifkan mode streaming
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
process.stdout.write('AI: ');
|
|
78
|
+
|
|
79
|
+
// Loop async iterator
|
|
80
|
+
for await (const chunk of stream) {
|
|
81
|
+
process.stdout.write(chunk);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('\n--- Selesai ---');
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('Error streaming:', error.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### 4. Generate Gambar
|
|
92
|
+
|
|
93
|
+
Membuat gambar berdasarkan deskripsi teks (prompt)
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
async function contohGenerateGambar() {
|
|
97
|
+
try {
|
|
98
|
+
const response = await client.image.generate({
|
|
99
|
+
model: 'flux', // Model gambar
|
|
100
|
+
prompt: 'Buatkan gambar galaksi bima sakti',
|
|
101
|
+
width: 1024,
|
|
102
|
+
height: 768
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
console.log('URL Gambar:', response.url);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('Gagal membuat gambar:', error.message);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 🛠️ Fitur Utama
|
|
113
|
+
|
|
114
|
+
- **Type-Safe**: Dibangun dengan TypeScript, memberikan autocompletion dan validasi tipe yang kuat
|
|
115
|
+
- **Convenience Methods**: API yang intuitif (`text.generate`, `image.generate`)
|
|
116
|
+
- **Streaming Support**: Dukungan native untuk Server-Sent Events (SSE) yang mudah digunakan
|
|
117
|
+
- **Multi-Model**: Akses ke berbagai model AI (OpenAI, Mistral, Gemini, Flux, dll) melalui satu interface
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## English Documentation
|
|
122
|
+
|
|
123
|
+
**@rethink-ai/core** is the official client library for integrating with ReThink Core AI services. It supports text generation (streaming & non-streaming) and image generation
|
|
124
|
+
|
|
125
|
+
### 📦 Installation
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm install @rethink-ai/core
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 🚀 Basic Usage
|
|
132
|
+
|
|
133
|
+
#### 1. Client Initialization
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { rethink } from '@rethink-ai/core';
|
|
137
|
+
|
|
138
|
+
const client = new rethink({
|
|
139
|
+
apiKey: 'YOUR_API_KEY_HERE'
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### 2. Text Generation
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const response = await client.text.generate({
|
|
147
|
+
model: 'rethink-v1',
|
|
148
|
+
prompt: 'Explain AI in one sentence.'
|
|
149
|
+
});
|
|
150
|
+
console.log(response.content);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### 3. Text Streaming
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
const stream = await client.text.generate({
|
|
157
|
+
model: 'rethink-v1',
|
|
158
|
+
prompt: 'Write a haiku about code.',
|
|
159
|
+
stream: true
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
for await (const chunk of stream) {
|
|
163
|
+
process.stdout.write(chunk);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### 4. Image Generation
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const response = await client.image.generate({
|
|
171
|
+
model: 'flux',
|
|
172
|
+
prompt: 'Generate an image of milky way',
|
|
173
|
+
width: 1024,
|
|
174
|
+
height: 768
|
|
175
|
+
});
|
|
176
|
+
console.log(response.url);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
<div align="center">
|
|
182
|
+
<p>Created with ❤️ by <b>R-Universe Labs</b></p>
|
|
183
|
+
<p>Rio Fernanda © 2026 ReThink AI. All rights reserved</p>
|
|
184
|
+
</div>
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RethinkConfig, TextGenerationRequest, TextGenerationResponse, ImageGenerationRequest, ImageGenerationResponse } from './types';
|
|
2
|
+
export declare class RethinkCoreClient {
|
|
3
|
+
private apiKey?;
|
|
4
|
+
private baseUrl;
|
|
5
|
+
constructor(config?: RethinkConfig);
|
|
6
|
+
private request;
|
|
7
|
+
private streamRequest;
|
|
8
|
+
text: {
|
|
9
|
+
generate: (request: TextGenerationRequest) => Promise<TextGenerationResponse> | AsyncGenerator<string, void, unknown>;
|
|
10
|
+
};
|
|
11
|
+
image: {
|
|
12
|
+
generate: (request: ImageGenerationRequest) => Promise<ImageGenerationResponse>;
|
|
13
|
+
};
|
|
14
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RethinkCoreClient = void 0;
|
|
4
|
+
class RethinkCoreClient {
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.text = {
|
|
7
|
+
generate: (request) => {
|
|
8
|
+
let messages = request.messages || [];
|
|
9
|
+
if (messages.length === 0) {
|
|
10
|
+
if (request.system) {
|
|
11
|
+
messages.push({ role: 'system', content: request.system });
|
|
12
|
+
}
|
|
13
|
+
if (request.prompt) {
|
|
14
|
+
messages.push({ role: 'user', content: request.prompt });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const payload = {
|
|
18
|
+
model: request.model,
|
|
19
|
+
messages,
|
|
20
|
+
stream: request.stream || false,
|
|
21
|
+
max_tokens: request.max_tokens,
|
|
22
|
+
temperature: request.temperature,
|
|
23
|
+
top_p: request.top_p
|
|
24
|
+
};
|
|
25
|
+
if (request.stream) {
|
|
26
|
+
return this.streamRequest('/v1/chat/completions', payload);
|
|
27
|
+
}
|
|
28
|
+
return this.request('/v1/chat/completions', {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
body: JSON.stringify(payload)
|
|
31
|
+
}).then(res => {
|
|
32
|
+
return {
|
|
33
|
+
content: res.choices?.[0]?.message?.content || '',
|
|
34
|
+
model: res.model,
|
|
35
|
+
usage: res.usage,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
this.image = {
|
|
41
|
+
generate: async (request) => {
|
|
42
|
+
return this.request('/api/generate/image', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
body: JSON.stringify(request)
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
this.apiKey = config.apiKey;
|
|
49
|
+
this.baseUrl = config.baseUrl || 'https://core.rethink.web.id';
|
|
50
|
+
}
|
|
51
|
+
async request(endpoint, options = {}) {
|
|
52
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
53
|
+
const headers = {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
...(options.headers || {})
|
|
56
|
+
};
|
|
57
|
+
if (this.apiKey) {
|
|
58
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
59
|
+
}
|
|
60
|
+
const response = await fetch(url, {
|
|
61
|
+
...options,
|
|
62
|
+
headers
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
let errorMessage = `Request failed to ${url} with status ${response.status}`;
|
|
66
|
+
try {
|
|
67
|
+
const errorBody = await response.json();
|
|
68
|
+
if (errorBody.error && errorBody.error.message) {
|
|
69
|
+
errorMessage += `: ${errorBody.error.message}`;
|
|
70
|
+
}
|
|
71
|
+
else if (errorBody.message) {
|
|
72
|
+
errorMessage += `: ${errorBody.message}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
}
|
|
77
|
+
throw new Error(errorMessage);
|
|
78
|
+
}
|
|
79
|
+
return response.json();
|
|
80
|
+
}
|
|
81
|
+
async *streamRequest(endpoint, body) {
|
|
82
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
83
|
+
const headers = {
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
'Accept': 'text/event-stream'
|
|
86
|
+
};
|
|
87
|
+
if (this.apiKey) {
|
|
88
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
89
|
+
}
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers,
|
|
93
|
+
body: JSON.stringify(body)
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
let errorMessage = `Stream request failed to ${url} with status ${response.status}`;
|
|
97
|
+
try {
|
|
98
|
+
const errorText = await response.text();
|
|
99
|
+
try {
|
|
100
|
+
const errorBody = JSON.parse(errorText);
|
|
101
|
+
if (errorBody.error && errorBody.error.message) {
|
|
102
|
+
errorMessage += `: ${errorBody.error.message}`;
|
|
103
|
+
}
|
|
104
|
+
else if (errorBody.message) {
|
|
105
|
+
errorMessage += `: ${errorBody.message}`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (jsonErr) {
|
|
109
|
+
errorMessage += `: ${errorText}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
}
|
|
114
|
+
throw new Error(errorMessage);
|
|
115
|
+
}
|
|
116
|
+
if (!response.body) {
|
|
117
|
+
throw new Error('No response body for stream');
|
|
118
|
+
}
|
|
119
|
+
const reader = response.body.getReader();
|
|
120
|
+
const decoder = new TextDecoder();
|
|
121
|
+
let buffer = '';
|
|
122
|
+
try {
|
|
123
|
+
while (true) {
|
|
124
|
+
const { done, value } = await reader.read();
|
|
125
|
+
if (done)
|
|
126
|
+
break;
|
|
127
|
+
buffer += decoder.decode(value, { stream: true });
|
|
128
|
+
const lines = buffer.split('\n\n');
|
|
129
|
+
buffer = lines.pop() || '';
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
const trimmed = line.trim();
|
|
132
|
+
if (!trimmed.startsWith('data: '))
|
|
133
|
+
continue;
|
|
134
|
+
const data = trimmed.slice(6);
|
|
135
|
+
if (data === '[DONE]')
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(data);
|
|
139
|
+
const delta = parsed.choices?.[0]?.delta?.content;
|
|
140
|
+
if (delta) {
|
|
141
|
+
yield delta;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
reader.releaseLock();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.RethinkCoreClient = RethinkCoreClient;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
exports.rethink = void 0;
|
|
18
|
+
const client_1 = require("./client");
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
__exportStar(require("./client"), exports);
|
|
21
|
+
exports.rethink = client_1.RethinkCoreClient;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface RethinkConfig {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ChatMessage {
|
|
6
|
+
role: 'system' | 'user' | 'assistant';
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TextGenerationRequest {
|
|
10
|
+
model: string;
|
|
11
|
+
prompt?: string;
|
|
12
|
+
messages?: ChatMessage[];
|
|
13
|
+
system?: string;
|
|
14
|
+
stream?: boolean;
|
|
15
|
+
max_tokens?: number;
|
|
16
|
+
temperature?: number;
|
|
17
|
+
top_p?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface TextGenerationResponse {
|
|
20
|
+
content: string;
|
|
21
|
+
model: string;
|
|
22
|
+
ad?: string;
|
|
23
|
+
usage?: {
|
|
24
|
+
prompt_tokens: number;
|
|
25
|
+
completion_tokens: number;
|
|
26
|
+
total_tokens: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export interface ImageGenerationRequest {
|
|
30
|
+
model: string;
|
|
31
|
+
prompt: string;
|
|
32
|
+
width?: number;
|
|
33
|
+
height?: number;
|
|
34
|
+
seed?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ImageGenerationResponse {
|
|
37
|
+
url: string;
|
|
38
|
+
model: string;
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
ad?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface Model {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
modelId: string;
|
|
47
|
+
apiId: string;
|
|
48
|
+
type: string;
|
|
49
|
+
status: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
allowedTiers: string[];
|
|
52
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rethink-ai/core",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Official TypeScript SDK for ReThink Core API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"prepublishOnly": "npm run build"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"rethink",
|
|
14
|
+
"ai",
|
|
15
|
+
"sdk",
|
|
16
|
+
"wrapper"
|
|
17
|
+
],
|
|
18
|
+
"author": "R-Universe Labs",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.3.3"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {}
|
|
24
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RethinkConfig,
|
|
3
|
+
TextGenerationRequest,
|
|
4
|
+
TextGenerationResponse,
|
|
5
|
+
ImageGenerationRequest,
|
|
6
|
+
ImageGenerationResponse,
|
|
7
|
+
ChatMessage
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
export class RethinkCoreClient {
|
|
11
|
+
private apiKey?: string;
|
|
12
|
+
private baseUrl: string;
|
|
13
|
+
|
|
14
|
+
constructor(config: RethinkConfig = {}) {
|
|
15
|
+
this.apiKey = config.apiKey;
|
|
16
|
+
this.baseUrl = config.baseUrl || 'https://core.rethink.web.id';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
20
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
21
|
+
const headers: Record<string, string> = {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
...((options.headers as Record<string, string>) || {})
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if (this.apiKey) {
|
|
27
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
...options,
|
|
32
|
+
headers
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
let errorMessage = `Request failed to ${url} with status ${response.status}`;
|
|
37
|
+
try {
|
|
38
|
+
const errorBody = await response.json();
|
|
39
|
+
if (errorBody.error && errorBody.error.message) {
|
|
40
|
+
errorMessage += `: ${errorBody.error.message}`;
|
|
41
|
+
} else if (errorBody.message) {
|
|
42
|
+
errorMessage += `: ${errorBody.message}`;
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
}
|
|
46
|
+
throw new Error(errorMessage);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return response.json() as Promise<T>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private async *streamRequest(endpoint: string, body: any): AsyncGenerator<string, void, unknown> {
|
|
53
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
54
|
+
const headers: Record<string, string> = {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'Accept': 'text/event-stream'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (this.apiKey) {
|
|
60
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const response = await fetch(url, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers,
|
|
66
|
+
body: JSON.stringify(body)
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
let errorMessage = `Stream request failed to ${url} with status ${response.status}`;
|
|
71
|
+
try {
|
|
72
|
+
const errorText = await response.text();
|
|
73
|
+
try {
|
|
74
|
+
const errorBody = JSON.parse(errorText);
|
|
75
|
+
if (errorBody.error && errorBody.error.message) {
|
|
76
|
+
errorMessage += `: ${errorBody.error.message}`;
|
|
77
|
+
} else if (errorBody.message) {
|
|
78
|
+
errorMessage += `: ${errorBody.message}`;
|
|
79
|
+
}
|
|
80
|
+
} catch (jsonErr) {
|
|
81
|
+
errorMessage += `: ${errorText}`;
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
}
|
|
85
|
+
throw new Error(errorMessage);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!response.body) {
|
|
89
|
+
throw new Error('No response body for stream');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const reader = response.body.getReader();
|
|
93
|
+
const decoder = new TextDecoder();
|
|
94
|
+
let buffer = '';
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
while (true) {
|
|
98
|
+
const { done, value } = await reader.read();
|
|
99
|
+
if (done) break;
|
|
100
|
+
|
|
101
|
+
buffer += decoder.decode(value, { stream: true });
|
|
102
|
+
|
|
103
|
+
const lines = buffer.split('\n\n');
|
|
104
|
+
buffer = lines.pop() || '';
|
|
105
|
+
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
const trimmed = line.trim();
|
|
108
|
+
if (!trimmed.startsWith('data: ')) continue;
|
|
109
|
+
|
|
110
|
+
const data = trimmed.slice(6);
|
|
111
|
+
if (data === '[DONE]') return;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(data);
|
|
115
|
+
const delta = parsed.choices?.[0]?.delta?.content;
|
|
116
|
+
if (delta) {
|
|
117
|
+
yield delta;
|
|
118
|
+
}
|
|
119
|
+
} catch (e) {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} finally {
|
|
124
|
+
reader.releaseLock();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public text = {
|
|
129
|
+
generate: (request: TextGenerationRequest): Promise<TextGenerationResponse> | AsyncGenerator<string, void, unknown> => {
|
|
130
|
+
let messages: ChatMessage[] = request.messages || [];
|
|
131
|
+
|
|
132
|
+
if (messages.length === 0) {
|
|
133
|
+
if (request.system) {
|
|
134
|
+
messages.push({ role: 'system', content: request.system });
|
|
135
|
+
}
|
|
136
|
+
if (request.prompt) {
|
|
137
|
+
messages.push({ role: 'user', content: request.prompt });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const payload = {
|
|
142
|
+
model: request.model,
|
|
143
|
+
messages,
|
|
144
|
+
stream: request.stream || false,
|
|
145
|
+
max_tokens: request.max_tokens,
|
|
146
|
+
temperature: request.temperature,
|
|
147
|
+
top_p: request.top_p
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (request.stream) {
|
|
151
|
+
return this.streamRequest('/v1/chat/completions', payload);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return this.request<any>('/v1/chat/completions', {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
body: JSON.stringify(payload)
|
|
157
|
+
}).then(res => {
|
|
158
|
+
return {
|
|
159
|
+
content: res.choices?.[0]?.message?.content || '',
|
|
160
|
+
model: res.model,
|
|
161
|
+
usage: res.usage,
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
public image = {
|
|
168
|
+
generate: async (request: ImageGenerationRequest): Promise<ImageGenerationResponse> => {
|
|
169
|
+
return this.request<ImageGenerationResponse>('/api/generate/image', {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
body: JSON.stringify(request)
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface RethinkConfig {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ChatMessage {
|
|
7
|
+
role: 'system' | 'user' | 'assistant';
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TextGenerationRequest {
|
|
12
|
+
model: string;
|
|
13
|
+
prompt?: string;
|
|
14
|
+
messages?: ChatMessage[];
|
|
15
|
+
system?: string;
|
|
16
|
+
stream?: boolean;
|
|
17
|
+
max_tokens?: number;
|
|
18
|
+
temperature?: number;
|
|
19
|
+
top_p?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TextGenerationResponse {
|
|
23
|
+
content: string;
|
|
24
|
+
model: string;
|
|
25
|
+
ad?: string;
|
|
26
|
+
usage?: {
|
|
27
|
+
prompt_tokens: number;
|
|
28
|
+
completion_tokens: number;
|
|
29
|
+
total_tokens: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ImageGenerationRequest {
|
|
34
|
+
model: string;
|
|
35
|
+
prompt: string;
|
|
36
|
+
width?: number;
|
|
37
|
+
height?: number;
|
|
38
|
+
seed?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ImageGenerationResponse {
|
|
42
|
+
url: string;
|
|
43
|
+
model: string;
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
ad?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface Model {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
modelId: string;
|
|
53
|
+
apiId: string;
|
|
54
|
+
type: string;
|
|
55
|
+
status: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
allowedTiers: string[];
|
|
58
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*"
|
|
14
|
+
]
|
|
15
|
+
}
|