Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ declare module 'replicate' {
type Status = 'starting' | 'processing' | 'succeeded' | 'failed' | 'canceled';
type WebhookEventType = 'start' | 'output' | 'logs' | 'completed';

interface Page<T> {
previous?: string;
next?: string;
results: T[];
export interface ApiError extends Error {
request: Request;
response: Response;
}

export interface Collection {
Expand Down Expand Up @@ -58,6 +57,12 @@ declare module 'replicate' {

export type Training = Prediction;

interface Page<T> {
previous?: string;
next?: string;
results: T[];
}

export default class Replicate {
constructor(options: {
auth: string;
Expand Down
21 changes: 16 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const ApiError = require('./lib/error');

const collections = require('./lib/collections');
const models = require('./lib/models');
const predictions = require('./lib/predictions');
const trainings = require('./lib/trainings');

const packageJSON = require('./package.json');

/**
Expand Down Expand Up @@ -127,7 +130,7 @@ class Replicate {
* @param {object} [parameters.params] - Query parameters
* @param {object} [parameters.data] - Body parameters
* @returns {Promise<object>} - Resolves with the API response data
* @throws {Error} If the request failed
* @throws {ApiError} If the request failed
*/
async request(route, parameters) {
const { auth, baseUrl, userAgent } = this;
Expand All @@ -149,14 +152,22 @@ class Replicate {
'User-Agent': userAgent,
};

const response = await this.fetch(url, {
const options = {
method,
headers,
body: data ? JSON.stringify(data) : undefined,
});
};

const response = await this.fetch(url, options);

if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
const request = new Request(url, options);
const responseText = await response.text();
throw new ApiError(
`Request to ${url} failed with status ${response.status} ${response.statusText}: ${responseText}.`,
request,
response,
);
}

return response.json();
Expand All @@ -173,7 +184,7 @@ class Replicate {
* @param {Function} endpoint - Function that returns a promise for the next page of results
* @yields {object[]} Each page of results
*/
async *paginate(endpoint) {
async * paginate(endpoint) {
const response = await endpoint();
yield response.results;
if (response.next) {
Expand Down
23 changes: 23 additions & 0 deletions index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,29 @@ describe('Replicate client', () => {
});
}).rejects.toThrow('Invalid webhook URL');
});

test('Throws an error with details failing response is JSON', async () => {
nock(BASE_URL)
.post('/predictions')
.reply(400, {
status: 400,
detail: "Invalid input",
}, { "Content-Type": "application/json" })

try {
expect.assertions(2);

await client.predictions.create({
version: '5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa',
input: {
text: null,
},
});
} catch (error) {
expect(error.response.status).toBe(400);
expect(error.message).toContain("Invalid input")
}
})
// Add more tests for error handling, edge cases, etc.
});

Expand Down
17 changes: 17 additions & 0 deletions lib/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class ApiError extends Error {
/**
* Creates a representation of an API error.
* @param {string} message - Error message
* @param {Request} request - HTTP request
* @param {Response} response - HTTP response
* @returns {ApiError}
*/
constructor(message, request, response) {
super(message);
this.name = 'ApiError';
this.request = request;
this.response = response;
}
}

module.exports = ApiError;