@slates-integrations/anthropic 0.2.0-rc.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/README.md +53 -0
- package/docs/SPEC.md +113 -0
- package/logo.png +0 -0
- package/package.json +19 -0
- package/slate.json +22 -0
- package/src/auth.ts +24 -0
- package/src/config.ts +8 -0
- package/src/index.ts +33 -0
- package/src/lib/client.ts +632 -0
- package/src/lib/errors.ts +74 -0
- package/src/spec.ts +13 -0
- package/src/tools/count-tokens.ts +74 -0
- package/src/tools/get-organization.ts +36 -0
- package/src/tools/get-usage-report.ts +141 -0
- package/src/tools/index.ts +10 -0
- package/src/tools/list-models.ts +84 -0
- package/src/tools/manage-api-keys.ts +94 -0
- package/src/tools/manage-files.ts +149 -0
- package/src/tools/manage-message-batch.ts +132 -0
- package/src/tools/manage-organization-members.ts +149 -0
- package/src/tools/manage-workspaces.ts +219 -0
- package/src/tools/send-message.ts +196 -0
- package/src/triggers/inbound-webhook.ts +67 -0
- package/src/triggers/index.ts +3 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
import { createAxios } from 'slates';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
import type { AxiosInstance } from 'axios';
|
|
4
|
+
import { anthropicApiError } from './errors';
|
|
5
|
+
|
|
6
|
+
let FILES_API_BETA = 'files-api-2025-04-14';
|
|
7
|
+
|
|
8
|
+
let betaHeaders = (headers?: string[]) =>
|
|
9
|
+
headers && headers.length > 0 ? { 'anthropic-beta': headers.join(',') } : undefined;
|
|
10
|
+
|
|
11
|
+
let appendParam = (
|
|
12
|
+
params: URLSearchParams,
|
|
13
|
+
key: string,
|
|
14
|
+
value: string | number | boolean | undefined
|
|
15
|
+
) => {
|
|
16
|
+
if (value !== undefined) params.append(key, String(value));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
let appendArrayParam = (
|
|
20
|
+
params: URLSearchParams,
|
|
21
|
+
key: string,
|
|
22
|
+
values: string[] | undefined
|
|
23
|
+
) => {
|
|
24
|
+
for (let value of values ?? []) {
|
|
25
|
+
params.append(key, value);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface BatchResult {
|
|
30
|
+
batchId: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
processingStatus?: string;
|
|
33
|
+
requestCounts?: {
|
|
34
|
+
processing: number;
|
|
35
|
+
succeeded: number;
|
|
36
|
+
errored: number;
|
|
37
|
+
canceled: number;
|
|
38
|
+
expired: number;
|
|
39
|
+
};
|
|
40
|
+
createdAt?: string;
|
|
41
|
+
updatedAt?: string;
|
|
42
|
+
expiresAt?: string;
|
|
43
|
+
resultsUrl?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AnthropicFileResult {
|
|
47
|
+
fileId: string;
|
|
48
|
+
type?: string;
|
|
49
|
+
filename?: string;
|
|
50
|
+
mimeType?: string;
|
|
51
|
+
sizeBytes?: number;
|
|
52
|
+
createdAt?: string;
|
|
53
|
+
downloadable?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface AnthropicFileContent {
|
|
57
|
+
fileId: string;
|
|
58
|
+
contentBase64: string;
|
|
59
|
+
contentType?: string;
|
|
60
|
+
sizeBytes: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AnthropicReportResult {
|
|
64
|
+
data: Array<Record<string, unknown>>;
|
|
65
|
+
hasMore: boolean;
|
|
66
|
+
nextPage?: string | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class AnthropicClient {
|
|
70
|
+
private axios: AxiosInstance;
|
|
71
|
+
|
|
72
|
+
constructor(private config: { token: string; apiVersion: string }) {
|
|
73
|
+
this.axios = createAxios({
|
|
74
|
+
baseURL: 'https://api.anthropic.com',
|
|
75
|
+
headers: {
|
|
76
|
+
'x-api-key': config.token,
|
|
77
|
+
'anthropic-version': config.apiVersion,
|
|
78
|
+
'content-type': 'application/json'
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.axios.interceptors.response.use(
|
|
83
|
+
response => response,
|
|
84
|
+
error => Promise.reject(anthropicApiError(error))
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---- Messages API ----
|
|
89
|
+
|
|
90
|
+
async createMessage(params: {
|
|
91
|
+
model: string;
|
|
92
|
+
maxTokens: number;
|
|
93
|
+
messages: Array<{
|
|
94
|
+
role: 'user' | 'assistant';
|
|
95
|
+
content: string | Array<Record<string, unknown>>;
|
|
96
|
+
}>;
|
|
97
|
+
system?: string;
|
|
98
|
+
temperature?: number;
|
|
99
|
+
topK?: number;
|
|
100
|
+
topP?: number;
|
|
101
|
+
stopSequences?: string[];
|
|
102
|
+
tools?: Array<Record<string, unknown>>;
|
|
103
|
+
toolChoice?: Record<string, unknown>;
|
|
104
|
+
thinking?: Record<string, unknown>;
|
|
105
|
+
metadata?: Record<string, unknown>;
|
|
106
|
+
mcpServers?: Array<Record<string, unknown>>;
|
|
107
|
+
serviceTier?: string;
|
|
108
|
+
betaHeaders?: string[];
|
|
109
|
+
}): Promise<Record<string, unknown>> {
|
|
110
|
+
let body: Record<string, unknown> = {
|
|
111
|
+
model: params.model,
|
|
112
|
+
max_tokens: params.maxTokens,
|
|
113
|
+
messages: params.messages
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (params.system !== undefined) body.system = params.system;
|
|
117
|
+
if (params.temperature !== undefined) body.temperature = params.temperature;
|
|
118
|
+
if (params.topK !== undefined) body.top_k = params.topK;
|
|
119
|
+
if (params.topP !== undefined) body.top_p = params.topP;
|
|
120
|
+
if (params.stopSequences !== undefined) body.stop_sequences = params.stopSequences;
|
|
121
|
+
if (params.tools !== undefined) body.tools = params.tools;
|
|
122
|
+
if (params.toolChoice !== undefined) body.tool_choice = params.toolChoice;
|
|
123
|
+
if (params.thinking !== undefined) body.thinking = params.thinking;
|
|
124
|
+
if (params.metadata !== undefined) body.metadata = params.metadata;
|
|
125
|
+
if (params.mcpServers !== undefined) body.mcp_servers = params.mcpServers;
|
|
126
|
+
if (params.serviceTier !== undefined) body.service_tier = params.serviceTier;
|
|
127
|
+
|
|
128
|
+
let response = await this.axios.post('/v1/messages', body, {
|
|
129
|
+
headers: betaHeaders(params.betaHeaders)
|
|
130
|
+
});
|
|
131
|
+
return response.data;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---- Token Counting ----
|
|
135
|
+
|
|
136
|
+
async countTokens(params: {
|
|
137
|
+
model: string;
|
|
138
|
+
messages: Array<{
|
|
139
|
+
role: 'user' | 'assistant';
|
|
140
|
+
content: string | Array<Record<string, unknown>>;
|
|
141
|
+
}>;
|
|
142
|
+
system?: string;
|
|
143
|
+
tools?: Array<Record<string, unknown>>;
|
|
144
|
+
thinking?: Record<string, unknown>;
|
|
145
|
+
betaHeaders?: string[];
|
|
146
|
+
}): Promise<{ inputTokens: number }> {
|
|
147
|
+
let body: Record<string, unknown> = {
|
|
148
|
+
model: params.model,
|
|
149
|
+
messages: params.messages
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (params.system !== undefined) body.system = params.system;
|
|
153
|
+
if (params.tools !== undefined) body.tools = params.tools;
|
|
154
|
+
if (params.thinking !== undefined) body.thinking = params.thinking;
|
|
155
|
+
|
|
156
|
+
let response = await this.axios.post('/v1/messages/count_tokens', body, {
|
|
157
|
+
headers: betaHeaders(params.betaHeaders)
|
|
158
|
+
});
|
|
159
|
+
return { inputTokens: response.data.input_tokens };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ---- Models API ----
|
|
163
|
+
|
|
164
|
+
async listModels(params?: { limit?: number; afterId?: string; beforeId?: string }): Promise<{
|
|
165
|
+
models: Array<Record<string, unknown>>;
|
|
166
|
+
hasMore: boolean;
|
|
167
|
+
firstId?: string;
|
|
168
|
+
lastId?: string;
|
|
169
|
+
}> {
|
|
170
|
+
let queryParams: Record<string, string> = {};
|
|
171
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
172
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
173
|
+
if (params?.beforeId !== undefined) queryParams.before_id = params.beforeId;
|
|
174
|
+
|
|
175
|
+
let response = await this.axios.get('/v1/models', { params: queryParams });
|
|
176
|
+
return {
|
|
177
|
+
models: response.data.data,
|
|
178
|
+
hasMore: response.data.has_more,
|
|
179
|
+
firstId: response.data.first_id,
|
|
180
|
+
lastId: response.data.last_id
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async getModel(modelId: string): Promise<Record<string, unknown>> {
|
|
185
|
+
let response = await this.axios.get(`/v1/models/${modelId}`);
|
|
186
|
+
return response.data;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ---- Files API ----
|
|
190
|
+
|
|
191
|
+
async createFile(params: {
|
|
192
|
+
filename: string;
|
|
193
|
+
contentBase64: string;
|
|
194
|
+
mimeType?: string;
|
|
195
|
+
}): Promise<AnthropicFileResult> {
|
|
196
|
+
let fileBytes = Buffer.from(params.contentBase64, 'base64');
|
|
197
|
+
let formData = new FormData();
|
|
198
|
+
let blob = new Blob([fileBytes], {
|
|
199
|
+
type: params.mimeType ?? 'application/octet-stream'
|
|
200
|
+
});
|
|
201
|
+
formData.append('file', blob, params.filename);
|
|
202
|
+
|
|
203
|
+
let response = await this.axios.post('/v1/files', formData, {
|
|
204
|
+
headers: {
|
|
205
|
+
'anthropic-beta': FILES_API_BETA,
|
|
206
|
+
'Content-Type': 'multipart/form-data'
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
return this.normalizeFile(response.data);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async listFiles(params?: { limit?: number; afterId?: string; beforeId?: string }): Promise<{
|
|
213
|
+
files: Array<AnthropicFileResult>;
|
|
214
|
+
hasMore: boolean;
|
|
215
|
+
firstId?: string;
|
|
216
|
+
lastId?: string;
|
|
217
|
+
}> {
|
|
218
|
+
let queryParams: Record<string, string> = {};
|
|
219
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
220
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
221
|
+
if (params?.beforeId !== undefined) queryParams.before_id = params.beforeId;
|
|
222
|
+
|
|
223
|
+
let response = await this.axios.get('/v1/files', {
|
|
224
|
+
params: queryParams,
|
|
225
|
+
headers: { 'anthropic-beta': FILES_API_BETA }
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
files: (response.data.data as Array<Record<string, unknown>>).map(file =>
|
|
229
|
+
this.normalizeFile(file)
|
|
230
|
+
),
|
|
231
|
+
hasMore: response.data.has_more,
|
|
232
|
+
firstId: response.data.first_id,
|
|
233
|
+
lastId: response.data.last_id
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async getFile(fileId: string): Promise<AnthropicFileResult> {
|
|
238
|
+
let response = await this.axios.get(`/v1/files/${fileId}`, {
|
|
239
|
+
headers: { 'anthropic-beta': FILES_API_BETA }
|
|
240
|
+
});
|
|
241
|
+
return this.normalizeFile(response.data);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async downloadFile(fileId: string): Promise<AnthropicFileContent> {
|
|
245
|
+
let response = await this.axios.get(`/v1/files/${fileId}/content`, {
|
|
246
|
+
responseType: 'arraybuffer',
|
|
247
|
+
headers: { 'anthropic-beta': FILES_API_BETA }
|
|
248
|
+
});
|
|
249
|
+
let content = Buffer.from(response.data as ArrayBuffer);
|
|
250
|
+
let contentType = response.headers['content-type'];
|
|
251
|
+
return {
|
|
252
|
+
fileId,
|
|
253
|
+
contentBase64: content.toString('base64'),
|
|
254
|
+
contentType: typeof contentType === 'string' ? contentType : undefined,
|
|
255
|
+
sizeBytes: content.byteLength
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async deleteFile(fileId: string): Promise<{ fileId: string; type?: string }> {
|
|
260
|
+
let response = await this.axios.delete(`/v1/files/${fileId}`, {
|
|
261
|
+
headers: { 'anthropic-beta': FILES_API_BETA }
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
fileId: response.data.id,
|
|
265
|
+
type: response.data.type
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ---- Message Batches API ----
|
|
270
|
+
|
|
271
|
+
async createMessageBatch(
|
|
272
|
+
requests: Array<{
|
|
273
|
+
customId: string;
|
|
274
|
+
params: Record<string, unknown>;
|
|
275
|
+
}>,
|
|
276
|
+
betaHeaderValues?: string[]
|
|
277
|
+
): Promise<BatchResult> {
|
|
278
|
+
let body = {
|
|
279
|
+
requests: requests.map(r => ({
|
|
280
|
+
custom_id: r.customId,
|
|
281
|
+
params: r.params
|
|
282
|
+
}))
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
let response = await this.axios.post('/v1/messages/batches', body, {
|
|
286
|
+
headers: betaHeaders(betaHeaderValues)
|
|
287
|
+
});
|
|
288
|
+
return this.normalizeBatch(response.data);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async getMessageBatch(batchId: string): Promise<BatchResult> {
|
|
292
|
+
let response = await this.axios.get(`/v1/messages/batches/${batchId}`);
|
|
293
|
+
return this.normalizeBatch(response.data);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async listMessageBatches(params?: {
|
|
297
|
+
limit?: number;
|
|
298
|
+
afterId?: string;
|
|
299
|
+
beforeId?: string;
|
|
300
|
+
}): Promise<{ batches: Array<BatchResult>; hasMore: boolean }> {
|
|
301
|
+
let queryParams: Record<string, string> = {};
|
|
302
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
303
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
304
|
+
if (params?.beforeId !== undefined) queryParams.before_id = params.beforeId;
|
|
305
|
+
|
|
306
|
+
let response = await this.axios.get('/v1/messages/batches', { params: queryParams });
|
|
307
|
+
return {
|
|
308
|
+
batches: (response.data.data as Array<Record<string, unknown>>).map(
|
|
309
|
+
(b: Record<string, unknown>) => this.normalizeBatch(b)
|
|
310
|
+
),
|
|
311
|
+
hasMore: response.data.has_more
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async cancelMessageBatch(batchId: string): Promise<BatchResult> {
|
|
316
|
+
let response = await this.axios.post(`/v1/messages/batches/${batchId}/cancel`);
|
|
317
|
+
return this.normalizeBatch(response.data);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private normalizeBatch(data: Record<string, unknown>): BatchResult {
|
|
321
|
+
let counts = data.request_counts as
|
|
322
|
+
| {
|
|
323
|
+
processing: number;
|
|
324
|
+
succeeded: number;
|
|
325
|
+
errored: number;
|
|
326
|
+
canceled: number;
|
|
327
|
+
expired: number;
|
|
328
|
+
}
|
|
329
|
+
| undefined;
|
|
330
|
+
return {
|
|
331
|
+
batchId: data.id as string,
|
|
332
|
+
type: data.type as string | undefined,
|
|
333
|
+
processingStatus: data.processing_status as string | undefined,
|
|
334
|
+
requestCounts: counts,
|
|
335
|
+
createdAt: data.created_at as string | undefined,
|
|
336
|
+
updatedAt: data.updated_at as string | undefined,
|
|
337
|
+
expiresAt: data.expires_at as string | undefined,
|
|
338
|
+
resultsUrl: data.results_url as string | undefined
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private normalizeFile(data: Record<string, unknown>): AnthropicFileResult {
|
|
343
|
+
return {
|
|
344
|
+
fileId: data.id as string,
|
|
345
|
+
type: data.type as string | undefined,
|
|
346
|
+
filename: data.filename as string | undefined,
|
|
347
|
+
mimeType: data.mime_type as string | undefined,
|
|
348
|
+
sizeBytes: data.size_bytes as number | undefined,
|
|
349
|
+
createdAt: data.created_at as string | undefined,
|
|
350
|
+
downloadable: data.downloadable as boolean | undefined
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ---- Admin API: Organization ----
|
|
355
|
+
|
|
356
|
+
async getOrganization(): Promise<Record<string, unknown>> {
|
|
357
|
+
let response = await this.axios.get('/v1/organizations/me');
|
|
358
|
+
return response.data;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ---- Admin API: Members ----
|
|
362
|
+
|
|
363
|
+
async listMembers(params?: {
|
|
364
|
+
limit?: number;
|
|
365
|
+
afterId?: string;
|
|
366
|
+
}): Promise<{ members: Array<Record<string, unknown>>; hasMore: boolean }> {
|
|
367
|
+
let queryParams: Record<string, string> = {};
|
|
368
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
369
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
370
|
+
|
|
371
|
+
let response = await this.axios.get('/v1/organizations/users', { params: queryParams });
|
|
372
|
+
return {
|
|
373
|
+
members: response.data.data,
|
|
374
|
+
hasMore: response.data.has_more
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async updateMember(userId: string, role: string): Promise<Record<string, unknown>> {
|
|
379
|
+
let response = await this.axios.post(`/v1/organizations/users/${userId}`, { role });
|
|
380
|
+
return response.data;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async removeMember(userId: string): Promise<void> {
|
|
384
|
+
await this.axios.delete(`/v1/organizations/users/${userId}`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ---- Admin API: Invites ----
|
|
388
|
+
|
|
389
|
+
async createInvite(email: string, role: string): Promise<Record<string, unknown>> {
|
|
390
|
+
let response = await this.axios.post('/v1/organizations/invites', { email, role });
|
|
391
|
+
return response.data;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async listInvites(params?: {
|
|
395
|
+
limit?: number;
|
|
396
|
+
afterId?: string;
|
|
397
|
+
}): Promise<{ invites: Array<Record<string, unknown>>; hasMore: boolean }> {
|
|
398
|
+
let queryParams: Record<string, string> = {};
|
|
399
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
400
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
401
|
+
|
|
402
|
+
let response = await this.axios.get('/v1/organizations/invites', { params: queryParams });
|
|
403
|
+
return {
|
|
404
|
+
invites: response.data.data,
|
|
405
|
+
hasMore: response.data.has_more
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async getInvite(inviteId: string): Promise<Record<string, unknown>> {
|
|
410
|
+
let response = await this.axios.get(`/v1/organizations/invites/${inviteId}`);
|
|
411
|
+
return response.data;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async deleteInvite(inviteId: string): Promise<void> {
|
|
415
|
+
await this.axios.delete(`/v1/organizations/invites/${inviteId}`);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ---- Admin API: Workspaces ----
|
|
419
|
+
|
|
420
|
+
async createWorkspace(
|
|
421
|
+
name: string,
|
|
422
|
+
params?: Record<string, unknown>
|
|
423
|
+
): Promise<Record<string, unknown>> {
|
|
424
|
+
let response = await this.axios.post('/v1/organizations/workspaces', { name, ...params });
|
|
425
|
+
return response.data;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async listWorkspaces(params?: {
|
|
429
|
+
limit?: number;
|
|
430
|
+
afterId?: string;
|
|
431
|
+
includeArchived?: boolean;
|
|
432
|
+
}): Promise<{ workspaces: Array<Record<string, unknown>>; hasMore: boolean }> {
|
|
433
|
+
let queryParams: Record<string, string> = {};
|
|
434
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
435
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
436
|
+
if (params?.includeArchived !== undefined)
|
|
437
|
+
queryParams.include_archived = String(params.includeArchived);
|
|
438
|
+
|
|
439
|
+
let response = await this.axios.get('/v1/organizations/workspaces', {
|
|
440
|
+
params: queryParams
|
|
441
|
+
});
|
|
442
|
+
return {
|
|
443
|
+
workspaces: response.data.data,
|
|
444
|
+
hasMore: response.data.has_more
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async getWorkspace(workspaceId: string): Promise<Record<string, unknown>> {
|
|
449
|
+
let response = await this.axios.get(`/v1/organizations/workspaces/${workspaceId}`);
|
|
450
|
+
return response.data;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async updateWorkspace(
|
|
454
|
+
workspaceId: string,
|
|
455
|
+
params: Record<string, unknown>
|
|
456
|
+
): Promise<Record<string, unknown>> {
|
|
457
|
+
let response = await this.axios.post(
|
|
458
|
+
`/v1/organizations/workspaces/${workspaceId}`,
|
|
459
|
+
params
|
|
460
|
+
);
|
|
461
|
+
return response.data;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async archiveWorkspace(workspaceId: string): Promise<Record<string, unknown>> {
|
|
465
|
+
let response = await this.axios.post(`/v1/organizations/workspaces/${workspaceId}`, {
|
|
466
|
+
is_archived: true
|
|
467
|
+
});
|
|
468
|
+
return response.data;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ---- Admin API: Workspace Members ----
|
|
472
|
+
|
|
473
|
+
async addWorkspaceMember(
|
|
474
|
+
workspaceId: string,
|
|
475
|
+
userId: string,
|
|
476
|
+
role: string
|
|
477
|
+
): Promise<Record<string, unknown>> {
|
|
478
|
+
let response = await this.axios.post(
|
|
479
|
+
`/v1/organizations/workspaces/${workspaceId}/members`,
|
|
480
|
+
{
|
|
481
|
+
user_id: userId,
|
|
482
|
+
workspace_role: role
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
return response.data;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async listWorkspaceMembers(
|
|
489
|
+
workspaceId: string,
|
|
490
|
+
params?: {
|
|
491
|
+
limit?: number;
|
|
492
|
+
afterId?: string;
|
|
493
|
+
}
|
|
494
|
+
): Promise<{ members: Array<Record<string, unknown>>; hasMore: boolean }> {
|
|
495
|
+
let queryParams: Record<string, string> = {};
|
|
496
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
497
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
498
|
+
|
|
499
|
+
let response = await this.axios.get(
|
|
500
|
+
`/v1/organizations/workspaces/${workspaceId}/members`,
|
|
501
|
+
{ params: queryParams }
|
|
502
|
+
);
|
|
503
|
+
return {
|
|
504
|
+
members: response.data.data,
|
|
505
|
+
hasMore: response.data.has_more
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
async getWorkspaceMember(
|
|
510
|
+
workspaceId: string,
|
|
511
|
+
userId: string
|
|
512
|
+
): Promise<Record<string, unknown>> {
|
|
513
|
+
let response = await this.axios.get(
|
|
514
|
+
`/v1/organizations/workspaces/${workspaceId}/members/${userId}`
|
|
515
|
+
);
|
|
516
|
+
return response.data;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async updateWorkspaceMember(
|
|
520
|
+
workspaceId: string,
|
|
521
|
+
userId: string,
|
|
522
|
+
role: string
|
|
523
|
+
): Promise<Record<string, unknown>> {
|
|
524
|
+
let response = await this.axios.post(
|
|
525
|
+
`/v1/organizations/workspaces/${workspaceId}/members/${userId}`,
|
|
526
|
+
{
|
|
527
|
+
workspace_role: role
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
return response.data;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async removeWorkspaceMember(workspaceId: string, userId: string): Promise<void> {
|
|
534
|
+
await this.axios.delete(`/v1/organizations/workspaces/${workspaceId}/members/${userId}`);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// ---- Admin API: API Keys ----
|
|
538
|
+
|
|
539
|
+
async listApiKeys(params?: {
|
|
540
|
+
limit?: number;
|
|
541
|
+
afterId?: string;
|
|
542
|
+
status?: string;
|
|
543
|
+
workspaceId?: string;
|
|
544
|
+
}): Promise<{ apiKeys: Array<Record<string, unknown>>; hasMore: boolean }> {
|
|
545
|
+
let queryParams: Record<string, string> = {};
|
|
546
|
+
if (params?.limit !== undefined) queryParams.limit = String(params.limit);
|
|
547
|
+
if (params?.afterId !== undefined) queryParams.after_id = params.afterId;
|
|
548
|
+
if (params?.status !== undefined) queryParams.status = params.status;
|
|
549
|
+
if (params?.workspaceId !== undefined) queryParams.workspace_id = params.workspaceId;
|
|
550
|
+
|
|
551
|
+
let response = await this.axios.get('/v1/organizations/api_keys', { params: queryParams });
|
|
552
|
+
return {
|
|
553
|
+
apiKeys: response.data.data,
|
|
554
|
+
hasMore: response.data.has_more
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async getApiKey(apiKeyId: string): Promise<Record<string, unknown>> {
|
|
559
|
+
let response = await this.axios.get(`/v1/organizations/api_keys/${apiKeyId}`);
|
|
560
|
+
return response.data;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async updateApiKey(
|
|
564
|
+
apiKeyId: string,
|
|
565
|
+
params: Record<string, unknown>
|
|
566
|
+
): Promise<Record<string, unknown>> {
|
|
567
|
+
let response = await this.axios.post(`/v1/organizations/api_keys/${apiKeyId}`, params);
|
|
568
|
+
return response.data;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// ---- Admin API: Usage and Cost Reports ----
|
|
572
|
+
|
|
573
|
+
async getMessagesUsageReport(params: {
|
|
574
|
+
startingAt: string;
|
|
575
|
+
endingAt?: string;
|
|
576
|
+
bucketWidth?: '1m' | '1h' | '1d';
|
|
577
|
+
limit?: number;
|
|
578
|
+
page?: string;
|
|
579
|
+
groupBy?: string[];
|
|
580
|
+
apiKeyIds?: string[];
|
|
581
|
+
workspaceIds?: string[];
|
|
582
|
+
models?: string[];
|
|
583
|
+
serviceTiers?: string[];
|
|
584
|
+
contextWindows?: string[];
|
|
585
|
+
}): Promise<AnthropicReportResult> {
|
|
586
|
+
let queryParams = new URLSearchParams();
|
|
587
|
+
appendParam(queryParams, 'starting_at', params.startingAt);
|
|
588
|
+
appendParam(queryParams, 'ending_at', params.endingAt);
|
|
589
|
+
appendParam(queryParams, 'bucket_width', params.bucketWidth);
|
|
590
|
+
appendParam(queryParams, 'limit', params.limit);
|
|
591
|
+
appendParam(queryParams, 'page', params.page);
|
|
592
|
+
appendArrayParam(queryParams, 'group_by[]', params.groupBy);
|
|
593
|
+
appendArrayParam(queryParams, 'api_key_ids[]', params.apiKeyIds);
|
|
594
|
+
appendArrayParam(queryParams, 'workspace_ids[]', params.workspaceIds);
|
|
595
|
+
appendArrayParam(queryParams, 'models[]', params.models);
|
|
596
|
+
appendArrayParam(queryParams, 'service_tiers[]', params.serviceTiers);
|
|
597
|
+
appendArrayParam(queryParams, 'context_window[]', params.contextWindows);
|
|
598
|
+
|
|
599
|
+
let response = await this.axios.get('/v1/organizations/usage_report/messages', {
|
|
600
|
+
params: queryParams
|
|
601
|
+
});
|
|
602
|
+
return {
|
|
603
|
+
data: response.data.data,
|
|
604
|
+
hasMore: response.data.has_more,
|
|
605
|
+
nextPage: response.data.next_page
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
async getCostReport(params: {
|
|
610
|
+
startingAt: string;
|
|
611
|
+
endingAt?: string;
|
|
612
|
+
limit?: number;
|
|
613
|
+
page?: string;
|
|
614
|
+
groupBy?: string[];
|
|
615
|
+
}): Promise<AnthropicReportResult> {
|
|
616
|
+
let queryParams = new URLSearchParams();
|
|
617
|
+
appendParam(queryParams, 'starting_at', params.startingAt);
|
|
618
|
+
appendParam(queryParams, 'ending_at', params.endingAt);
|
|
619
|
+
appendParam(queryParams, 'limit', params.limit);
|
|
620
|
+
appendParam(queryParams, 'page', params.page);
|
|
621
|
+
appendArrayParam(queryParams, 'group_by[]', params.groupBy);
|
|
622
|
+
|
|
623
|
+
let response = await this.axios.get('/v1/organizations/cost_report', {
|
|
624
|
+
params: queryParams
|
|
625
|
+
});
|
|
626
|
+
return {
|
|
627
|
+
data: response.data.data,
|
|
628
|
+
hasMore: response.data.has_more,
|
|
629
|
+
nextPage: response.data.next_page
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ServiceError, badRequestError } from '@lowerdeck/error';
|
|
2
|
+
|
|
3
|
+
type ErrorResponse = {
|
|
4
|
+
status?: number;
|
|
5
|
+
statusText?: string;
|
|
6
|
+
data?: unknown;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
10
|
+
typeof value === 'object' && value !== null;
|
|
11
|
+
|
|
12
|
+
let addMessage = (messages: string[], value: unknown) => {
|
|
13
|
+
if (typeof value !== 'string') return;
|
|
14
|
+
let trimmed = value.trim();
|
|
15
|
+
if (trimmed && !messages.includes(trimmed)) {
|
|
16
|
+
messages.push(trimmed);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let extractAnthropicMessage = (error: unknown) => {
|
|
21
|
+
let response = isRecord(error) ? (error.response as ErrorResponse | undefined) : undefined;
|
|
22
|
+
let data = response?.data;
|
|
23
|
+
let messages: string[] = [];
|
|
24
|
+
|
|
25
|
+
if (isRecord(data)) {
|
|
26
|
+
addMessage(messages, data.message);
|
|
27
|
+
addMessage(messages, data.error);
|
|
28
|
+
|
|
29
|
+
if (isRecord(data.error)) {
|
|
30
|
+
addMessage(messages, data.error.message);
|
|
31
|
+
addMessage(messages, data.error.type);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
addMessage(messages, data);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (messages.length > 0) {
|
|
38
|
+
return messages.join(' - ');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (error instanceof Error && error.message) {
|
|
42
|
+
return error.message;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return 'Unknown error';
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export let anthropicServiceError = (message: string) =>
|
|
49
|
+
new ServiceError(badRequestError({ message }));
|
|
50
|
+
|
|
51
|
+
export let anthropicApiError = (error: unknown, operation = 'request') => {
|
|
52
|
+
if (error instanceof ServiceError) {
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let response = isRecord(error) ? (error.response as ErrorResponse | undefined) : undefined;
|
|
57
|
+
let status = response?.status;
|
|
58
|
+
let statusLabel =
|
|
59
|
+
status !== undefined
|
|
60
|
+
? `HTTP ${status}${response?.statusText ? ` ${response.statusText}` : ''}: `
|
|
61
|
+
: '';
|
|
62
|
+
|
|
63
|
+
let serviceError = anthropicServiceError(
|
|
64
|
+
`Anthropic API ${operation} failed: ${statusLabel}${extractAnthropicMessage(error)}`
|
|
65
|
+
);
|
|
66
|
+
serviceError.data.reason = 'anthropic_api_error';
|
|
67
|
+
serviceError.data.upstreamStatus = status;
|
|
68
|
+
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
serviceError.setParent(error);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return serviceError;
|
|
74
|
+
};
|