This repository was archived by the owner on Nov 20, 2024. It is now read-only.
Repository navigation
Expand file tree
/
Copy pathscholarly_api_latest.js
More file actions
230 lines (208 loc) · 7.06 KB
/
Copy pathscholarly_api_latest.js
File metadata and controls
230 lines (208 loc) · 7.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
const baseUrl = 'https://legacy.scholarly.digital/v1/';
/**
* Represents an error for invalid arguments passed to a function.
*/
export class InvalidArgumentError extends Error {
constructor(message) {
super(message);
this.name = 'InvalidArgumentError';
}
}
/**
* Represents a custom error class for timeouts.
*/
export class TimeoutError extends Error {
constructor(message) {
super(message);
this.name = 'TimeoutError';
}
}
/**
* Checks the response for possible errors and throws appropriate exceptions.
* @param {Response} response - The response object to be checked for errors.
*/
function checkError(response) {
if (response.status === 400) {
throw new InvalidArgumentError(`Invalid argument. Status: ${response.status}`);
}
if (!response.ok) {
throw new Error(`Failed request. Status: ${response.status}`);
}
}
/**
* Fetches a new session ID.
* @param {string} profile - The profile for Coach.
* @param {number} timeout - The request timeout in milliseconds.
* @returns {Promise} A promise that resolves to the JSON response containing the session ID.
*/
export async function getSession(profile, timeout) {
const endpoint = 'get-session';
let url = baseUrl + endpoint;
if (profile === undefined) profile = "default";
const data = '{"profile":"' + profile + '"}';
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: data,
signal: AbortSignal.timeout(timeout),
});
checkError(response);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new TimeoutError(`getSession Error: ${error.message}`);
}
throw error;
}
}
/**
* Fetches the messages for the provided session ID.
* @param {string} session - The session ID.
* @param {number} timeout - The request timeout in milliseconds.
* @returns {Promise} A promise that resolves to the JSON response containing the messages for the session.
*/
export async function getMessages(session, timeout) {
const endpoint = 'get-messages';
let url = baseUrl + endpoint;
const data = '{"session":"' + session + '"}';
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: data,
signal: AbortSignal.timeout(timeout),
});
checkError(response);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new TimeoutError(`getMessages Error: ${error.message}`);
}
throw error;
}
}
/**
* Fetches the coach stream for the given prompt and session ID, returning an async generator with content chunks.
* @param {string} prompt - The prompt to send to the coach stream.
* @param {string} session - The session ID.
* @param {AbortSignal} signal - The optional AbortSignal for handling cancellations.
* @returns {AsyncGenerator} An async generator that yields content chunks from the coach stream.
*/
async function* fetchCoachStream(prompt, session, imageInput = null, signal) {
const endpoint = 'coach';
let url = baseUrl + endpoint;
const formData = new FormData();
formData.append('json', new Blob([JSON.stringify({
prompt: prompt,
session: session,
stream: true,
})], { type: 'application/json' }));
if (imageInput instanceof File) {
formData.append('image', imageInput);
}
const response = await fetch(url, {
method: 'POST',
body: formData,
signal: signal
});
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
try {
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
const content = decoder.decode(value, { stream: true });
yield content;
if (signal.aborted) {
break;
}
}
} catch (error) {
if (error.name === 'AbortError' && reader) {
await reader.cancel();
}
throw error;
}
}
/**
* Fetches a response from Coach.
* @param {string} prompt - The prompt to send to coach.
* @param {string} session - The session ID.
* @param {File|null} imageInput - Optional file input to an image file for Coach to analyse. Defaults to null.
* @param {number} timeout - The request timeout in milliseconds.
* @returns {Promise} A promise that resolves to the coach response.
*/
export async function coachResponse(prompt, session, imageInput = null, timeout) {
const endpoint = 'coach';
let url = baseUrl + endpoint;
const formData = new FormData();
formData.append('json', new Blob([JSON.stringify({
prompt: prompt,
session: session,
stream: false,
})], { type: 'application/json' }));
if (imageInput instanceof File) {
formData.append('image', imageInput);
}
try {
const response = await fetch(url, {
method: 'POST',
body: formData,
signal: AbortSignal.timeout(timeout),
});
checkError(response);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new TimeoutError(`coachResponse Error: ${error.message}`);
}
throw error;
}
}
/**
* Streams content from the coach API given a prompt and sessionID.
* @param {string} prompt - The prompt to send to coach.
* @param {string} session - The session ID. Defaults to a new session.
* @param {File|null} imageInput - Optional file input to an image file for Coach to analyse. Defaults to null.
* @param {function} onContentReceived - Callback function for handling content received.
* @param {function} onStreamFinished - Callback function for when the stream has finished.
* @param {function} onError - Callback function for handling errors during streaming.
* @param {AbortSignal} signal - The optional AbortSignal for handling cancellations.
*/
export async function coachStream(prompt, session=getSession(null), imageInput = null, onContentReceived, onStreamFinished, onError, signal) {
const abortController = new AbortController();
const onStopped = (manual) => {
if (onStreamFinished) {
onStreamFinished(manual);
}
};
try {
for await (const content of fetchCoachStream(prompt, session, imageInput, signal ? signal : abortController.signal)) {
onContentReceived(content);
if (signal && signal.aborted) {
abortController.abort();
}
}
onStopped(false);
}
catch (err) {
if (err.name === 'AbortError') {
onStopped(true);
} else {
onError(err);
onStreamFinished(false);
}
}
};