@acedatacloud/sdk 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.
Files changed (47) hide show
  1. package/README.md +97 -0
  2. package/dist/client.d.ts +31 -0
  3. package/dist/client.js +46 -0
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +20 -0
  6. package/dist/resources/audio.d.ts +17 -0
  7. package/dist/resources/audio.js +30 -0
  8. package/dist/resources/chat.d.ts +30 -0
  9. package/dist/resources/chat.js +38 -0
  10. package/dist/resources/files.d.ts +9 -0
  11. package/dist/resources/files.js +59 -0
  12. package/dist/resources/images.d.ts +18 -0
  13. package/dist/resources/images.js +32 -0
  14. package/dist/resources/openai.d.ts +46 -0
  15. package/dist/resources/openai.js +59 -0
  16. package/dist/resources/platform.d.ts +41 -0
  17. package/dist/resources/platform.js +76 -0
  18. package/dist/resources/search.d.ts +14 -0
  19. package/dist/resources/search.js +22 -0
  20. package/dist/resources/tasks.d.ts +14 -0
  21. package/dist/resources/tasks.js +36 -0
  22. package/dist/resources/video.d.ts +17 -0
  23. package/dist/resources/video.js +30 -0
  24. package/dist/runtime/errors.d.ts +44 -0
  25. package/dist/runtime/errors.js +89 -0
  26. package/dist/runtime/index.d.ts +3 -0
  27. package/dist/runtime/index.js +19 -0
  28. package/dist/runtime/tasks.d.ts +17 -0
  29. package/dist/runtime/tasks.js +46 -0
  30. package/dist/runtime/transport.d.ts +31 -0
  31. package/dist/runtime/transport.js +210 -0
  32. package/package.json +28 -0
  33. package/src/client.ts +56 -0
  34. package/src/index.ts +19 -0
  35. package/src/resources/audio.ts +34 -0
  36. package/src/resources/chat.ts +63 -0
  37. package/src/resources/files.ts +27 -0
  38. package/src/resources/images.ts +36 -0
  39. package/src/resources/openai.ts +96 -0
  40. package/src/resources/platform.ts +77 -0
  41. package/src/resources/search.ts +23 -0
  42. package/src/resources/tasks.ts +38 -0
  43. package/src/resources/video.ts +34 -0
  44. package/src/runtime/errors.ts +93 -0
  45. package/src/runtime/index.ts +15 -0
  46. package/src/runtime/tasks.ts +57 -0
  47. package/src/runtime/transport.ts +257 -0
@@ -0,0 +1,257 @@
1
+ /** HTTP transport for AceDataCloud SDK. Uses native fetch (Node 18+). */
2
+
3
+ import {
4
+ APIError,
5
+ AuthenticationError,
6
+ InsufficientBalanceError,
7
+ ModerationError,
8
+ RateLimitError,
9
+ ResourceDisabledError,
10
+ TimeoutError,
11
+ TokenMismatchError,
12
+ TransportError,
13
+ ValidationError,
14
+ } from './errors';
15
+
16
+ const ERROR_CODE_MAP: Record<string, typeof APIError> = {
17
+ invalid_token: AuthenticationError,
18
+ token_expired: AuthenticationError,
19
+ no_token: AuthenticationError,
20
+ token_mismatched: TokenMismatchError,
21
+ used_up: InsufficientBalanceError,
22
+ disabled: ResourceDisabledError,
23
+ too_many_requests: RateLimitError,
24
+ bad_request: ValidationError,
25
+ };
26
+
27
+ const RETRY_STATUS_CODES = new Set([408, 409, 429, 500, 502, 503, 504]);
28
+
29
+ function mapError(statusCode: number, body: Record<string, unknown>): APIError {
30
+ const errorData = (body.error ?? {}) as Record<string, unknown>;
31
+ const code = (errorData.code ?? '') as string;
32
+ const message = (errorData.message ?? '') as string;
33
+ const traceId = body.trace_id as string | undefined;
34
+
35
+ let ErrorClass = ERROR_CODE_MAP[code];
36
+ if (!ErrorClass) {
37
+ if (statusCode === 403) ErrorClass = ModerationError;
38
+ else if (statusCode === 401) ErrorClass = AuthenticationError;
39
+ else if (statusCode === 429) ErrorClass = RateLimitError;
40
+ else if (statusCode === 400) ErrorClass = ValidationError;
41
+ else ErrorClass = APIError;
42
+ }
43
+
44
+ return new ErrorClass({ message, statusCode, code, traceId, body });
45
+ }
46
+
47
+ function backoffDelay(attempt: number): number {
48
+ const base = Math.min(2 ** attempt, 8);
49
+ return base + Math.random() * 0.5;
50
+ }
51
+
52
+ function sleep(ms: number): Promise<void> {
53
+ return new Promise((resolve) => setTimeout(resolve, ms));
54
+ }
55
+
56
+ export interface TransportOptions {
57
+ apiToken?: string;
58
+ baseURL?: string;
59
+ platformBaseURL?: string;
60
+ timeout?: number;
61
+ maxRetries?: number;
62
+ headers?: Record<string, string>;
63
+ }
64
+
65
+ export class Transport {
66
+ private baseURL: string;
67
+ private platformBaseURL: string;
68
+ private timeout: number;
69
+ private maxRetries: number;
70
+ private headers: Record<string, string>;
71
+
72
+ constructor(opts: TransportOptions = {}) {
73
+ const token = opts.apiToken ?? process.env.ACEDATACLOUD_API_TOKEN ?? '';
74
+ if (!token) {
75
+ throw new AuthenticationError({
76
+ message: 'apiToken is required. Pass it to the client or set ACEDATACLOUD_API_TOKEN.',
77
+ statusCode: 0,
78
+ code: 'no_token',
79
+ });
80
+ }
81
+ this.baseURL = (opts.baseURL ?? 'https://api.acedata.cloud').replace(/\/+$/, '');
82
+ this.platformBaseURL = (opts.platformBaseURL ?? 'https://platform.acedata.cloud').replace(/\/+$/, '');
83
+ this.timeout = opts.timeout ?? 300_000;
84
+ this.maxRetries = opts.maxRetries ?? 2;
85
+ this.headers = {
86
+ accept: 'application/json',
87
+ authorization: `Bearer ${token}`,
88
+ 'content-type': 'application/json',
89
+ 'user-agent': 'acedatacloud-node/0.1.0',
90
+ ...(opts.headers ?? {}),
91
+ };
92
+ }
93
+
94
+ async request(
95
+ method: string,
96
+ path: string,
97
+ opts: {
98
+ json?: Record<string, unknown>;
99
+ params?: Record<string, string>;
100
+ platform?: boolean;
101
+ timeout?: number;
102
+ headers?: Record<string, string>;
103
+ } = {}
104
+ ): Promise<Record<string, unknown>> {
105
+ const base = opts.platform ? this.platformBaseURL : this.baseURL;
106
+ let url = `${base}${path}`;
107
+ if (opts.params) {
108
+ const qs = new URLSearchParams(opts.params).toString();
109
+ url += `?${qs}`;
110
+ }
111
+ const headers = { ...this.headers, ...(opts.headers ?? {}) };
112
+ const timeoutMs = opts.timeout ?? this.timeout;
113
+
114
+ let lastError: Error | null = null;
115
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
116
+ const controller = new AbortController();
117
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
118
+ try {
119
+ const resp = await fetch(url, {
120
+ method,
121
+ headers,
122
+ body: opts.json ? JSON.stringify(opts.json) : undefined,
123
+ signal: controller.signal,
124
+ });
125
+ clearTimeout(timer);
126
+
127
+ if (resp.status >= 400) {
128
+ let body: Record<string, unknown>;
129
+ try {
130
+ body = (await resp.json()) as Record<string, unknown>;
131
+ } catch {
132
+ body = { error: { code: 'unknown', message: await resp.text() } };
133
+ }
134
+ if (RETRY_STATUS_CODES.has(resp.status) && attempt < this.maxRetries) {
135
+ await sleep(backoffDelay(attempt) * 1000);
136
+ continue;
137
+ }
138
+ throw mapError(resp.status, body);
139
+ }
140
+
141
+ return (await resp.json()) as Record<string, unknown>;
142
+ } catch (err) {
143
+ clearTimeout(timer);
144
+ if (err instanceof APIError) throw err;
145
+ lastError = err as Error;
146
+ if (attempt < this.maxRetries) {
147
+ await sleep(backoffDelay(attempt) * 1000);
148
+ continue;
149
+ }
150
+ }
151
+ }
152
+ throw lastError ?? new TransportError('Request failed after retries');
153
+ }
154
+
155
+ async *requestStream(
156
+ method: string,
157
+ path: string,
158
+ opts: { json?: Record<string, unknown>; timeout?: number } = {}
159
+ ): AsyncGenerator<string, void, unknown> {
160
+ const url = `${this.baseURL}${path}`;
161
+ const headers = { ...this.headers, accept: 'text/event-stream' };
162
+ const controller = new AbortController();
163
+ const timer = setTimeout(() => controller.abort(), opts.timeout ?? this.timeout);
164
+
165
+ try {
166
+ const resp = await fetch(url, {
167
+ method,
168
+ headers,
169
+ body: opts.json ? JSON.stringify(opts.json) : undefined,
170
+ signal: controller.signal,
171
+ });
172
+
173
+ if (resp.status >= 400) {
174
+ let body: Record<string, unknown>;
175
+ try {
176
+ body = (await resp.json()) as Record<string, unknown>;
177
+ } catch {
178
+ body = { error: { code: 'unknown', message: '' } };
179
+ }
180
+ throw mapError(resp.status, body);
181
+ }
182
+
183
+ if (!resp.body) throw new TransportError('No response body for stream');
184
+
185
+ const reader = resp.body.getReader();
186
+ const decoder = new TextDecoder();
187
+ let buffer = '';
188
+
189
+ while (true) {
190
+ const { done, value } = await reader.read();
191
+ if (done) break;
192
+ buffer += decoder.decode(value, { stream: true });
193
+
194
+ const lines = buffer.split('\n');
195
+ buffer = lines.pop() ?? '';
196
+
197
+ for (const line of lines) {
198
+ if (line.startsWith('data: ')) {
199
+ const data = line.slice(6);
200
+ if (data === '[DONE]') return;
201
+ yield data;
202
+ }
203
+ }
204
+ }
205
+ } finally {
206
+ clearTimeout(timer);
207
+ }
208
+ }
209
+
210
+ async upload(
211
+ path: string,
212
+ fileData: Buffer | Uint8Array,
213
+ filename: string,
214
+ opts: { timeout?: number } = {}
215
+ ): Promise<Record<string, unknown>> {
216
+ const url = `${this.platformBaseURL}${path}`;
217
+ const boundary = `----AceDataCloudBoundary${Date.now()}`;
218
+ const headers = {
219
+ ...this.headers,
220
+ 'content-type': `multipart/form-data; boundary=${boundary}`,
221
+ };
222
+ delete (headers as Record<string, string>)['content-type'];
223
+
224
+ const body = new FormData();
225
+ body.append('file', new Blob([fileData]), filename);
226
+
227
+ const authHeaders: Record<string, string> = {
228
+ authorization: this.headers.authorization,
229
+ 'user-agent': this.headers['user-agent'],
230
+ };
231
+
232
+ const controller = new AbortController();
233
+ const timer = setTimeout(() => controller.abort(), opts.timeout ?? this.timeout);
234
+ try {
235
+ const resp = await fetch(url, {
236
+ method: 'POST',
237
+ headers: authHeaders,
238
+ body,
239
+ signal: controller.signal,
240
+ });
241
+ clearTimeout(timer);
242
+
243
+ if (resp.status >= 400) {
244
+ let respBody: Record<string, unknown>;
245
+ try {
246
+ respBody = (await resp.json()) as Record<string, unknown>;
247
+ } catch {
248
+ respBody = { error: { code: 'unknown', message: await resp.text() } };
249
+ }
250
+ throw mapError(resp.status, respBody);
251
+ }
252
+ return (await resp.json()) as Record<string, unknown>;
253
+ } finally {
254
+ clearTimeout(timer);
255
+ }
256
+ }
257
+ }