Commit c1634b05 authored by Travis Fischer's avatar Travis Fischer

feat: add additional options to better control debugging; add some inline docs

parent 2e654be5
...@@ -16,27 +16,32 @@ export class ChatGPTAPI { ...@@ -16,27 +16,32 @@ export class ChatGPTAPI {
protected _sessionToken: string protected _sessionToken: string
protected _clearanceToken: string protected _clearanceToken: string
protected _markdown: boolean protected _markdown: boolean
protected _debug: boolean
protected _apiBaseUrl: string protected _apiBaseUrl: string
protected _backendApiBaseUrl: string protected _backendApiBaseUrl: string
protected _userAgent: string protected _userAgent: string
protected _headers: Record<string, string> protected _headers: Record<string, string>
protected _user: types.User | null = null
// Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh // Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh
protected _accessTokenCache: ExpiryMap<string, string> protected _accessTokenCache: ExpiryMap<string, string>
protected _user: types.User | null = null
/** /**
* Creates a new client wrapper around the unofficial ChatGPT REST API. * Creates a new client wrapper around the unofficial ChatGPT REST API.
* *
* @param opts.sessionToken = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions) * @param opts.sessionToken = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions)
* @param opts.clearanceToken = **Required** Cloudflare `cf_clearance` cookie value (see readme for instructions)
* @param apiBaseUrl - Optional override; the base URL for ChatGPT webapp's API (`/api`) * @param apiBaseUrl - Optional override; the base URL for ChatGPT webapp's API (`/api`)
* @param backendApiBaseUrl - Optional override; the base URL for the ChatGPT backend API (`/backend-api`) * @param backendApiBaseUrl - Optional override; the base URL for the ChatGPT backend API (`/backend-api`)
* @param userAgent - Optional override; the `user-agent` header to use with ChatGPT requests * @param userAgent - Optional override; the `user-agent` header to use with ChatGPT requests
* @param accessTokenTTL - Optional override; how long in milliseconds access tokens should last before being forcefully refreshed * @param accessTokenTTL - Optional override; how long in milliseconds access tokens should last before being forcefully refreshed
* @param accessToken - Optional default access token if you already have a valid one generated
* @param heaaders - Optional additional HTTP headers to be added to each `fetch` request
* @param debug - Optional enables logging debugging into to stdout
*/ */
constructor(opts: { constructor(opts: {
sessionToken: string sessionToken: string
clearanceToken: string clearanceToken: string
/** @defaultValue `true` **/ /** @defaultValue `true` **/
...@@ -48,15 +53,20 @@ export class ChatGPTAPI { ...@@ -48,15 +53,20 @@ export class ChatGPTAPI {
/** @defaultValue `'https://chat.openai.com/backend-api'` **/ /** @defaultValue `'https://chat.openai.com/backend-api'` **/
backendApiBaseUrl?: string backendApiBaseUrl?: string
/** @defaultValue `'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'` **/ /** @defaultValue `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'` **/
userAgent?: string userAgent?: string
/** @defaultValue 1 hour */ /** @defaultValue 1 hour **/
accessTokenTTL?: number accessTokenTTL?: number
/** @defaultValue `undefined` **/
accessToken?: string accessToken?: string
/** @defaultValue `undefined` **/
headers?: Record<string, string> headers?: Record<string, string>
/** @defaultValue `false` **/
debug?: boolean
}) { }) {
const { const {
sessionToken, sessionToken,
...@@ -67,12 +77,14 @@ export class ChatGPTAPI { ...@@ -67,12 +77,14 @@ export class ChatGPTAPI {
userAgent = USER_AGENT, userAgent = USER_AGENT,
accessTokenTTL = 60 * 60000, // 1 hour accessTokenTTL = 60 * 60000, // 1 hour
accessToken, accessToken,
headers headers,
debug = false
} = opts } = opts
this._sessionToken = sessionToken this._sessionToken = sessionToken
this._clearanceToken = clearanceToken this._clearanceToken = clearanceToken
this._markdown = !!markdown this._markdown = !!markdown
this._debug = !!debug
this._apiBaseUrl = apiBaseUrl this._apiBaseUrl = apiBaseUrl
this._backendApiBaseUrl = backendApiBaseUrl this._backendApiBaseUrl = backendApiBaseUrl
this._userAgent = userAgent this._userAgent = userAgent
...@@ -124,6 +136,8 @@ export class ChatGPTAPI { ...@@ -124,6 +136,8 @@ export class ChatGPTAPI {
* @param message - The prompt message to send * @param message - The prompt message to send
* @param opts.conversationId - Optional ID of a conversation to continue * @param opts.conversationId - Optional ID of a conversation to continue
* @param opts.parentMessageId - Optional ID of the previous message in the conversation * @param opts.parentMessageId - Optional ID of the previous message in the conversation
* @param opts.messageId - Optional ID of the message to send (defaults to a random UUID)
* @param opts.action - Optional ChatGPT `action` (either `next` or `variant`)
* @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout) * @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout)
* @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
* @param opts.onConversationResponse - Optional callback which will be invoked every time the partial response is updated with the full conversation response * @param opts.onConversationResponse - Optional callback which will be invoked every time the partial response is updated with the full conversation response
...@@ -138,6 +152,8 @@ export class ChatGPTAPI { ...@@ -138,6 +152,8 @@ export class ChatGPTAPI {
const { const {
conversationId, conversationId,
parentMessageId = uuidv4(), parentMessageId = uuidv4(),
messageId = uuidv4(),
action = 'next',
timeoutMs, timeoutMs,
onProgress, onProgress,
onConversationResponse onConversationResponse
...@@ -154,10 +170,10 @@ export class ChatGPTAPI { ...@@ -154,10 +170,10 @@ export class ChatGPTAPI {
const accessToken = await this.refreshAccessToken() const accessToken = await this.refreshAccessToken()
const body: types.ConversationJSONBody = { const body: types.ConversationJSONBody = {
action: 'next', action,
messages: [ messages: [
{ {
id: uuidv4(), id: messageId,
role: 'user', role: 'user',
content: { content: {
content_type: 'text', content_type: 'text',
...@@ -173,19 +189,25 @@ export class ChatGPTAPI { ...@@ -173,19 +189,25 @@ export class ChatGPTAPI {
body.conversation_id = conversationId body.conversation_id = conversationId
} }
const url = `${this._backendApiBaseUrl}/conversation`
let response = '' let response = ''
const responseP = new Promise<string>((resolve, reject) => { const responseP = new Promise<string>((resolve, reject) => {
fetchSSE(url, { const url = `${this._backendApiBaseUrl}/conversation`
method: 'POST', const headers = {
headers: {
...this._headers, ...this._headers,
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
Accept: 'text/event-stream', Accept: 'text/event-stream',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Cookie: `cf_clearance=${this._clearanceToken}` Cookie: `cf_clearance=${this._clearanceToken}`
}, }
if (this._debug) {
console.log('POST', url, { body, headers })
}
fetchSSE(url, {
method: 'POST',
headers,
body: JSON.stringify(body), body: JSON.stringify(body),
signal: abortSignal, signal: abortSignal,
onMessage: (data: string) => { onMessage: (data: string) => {
...@@ -282,14 +304,18 @@ export class ChatGPTAPI { ...@@ -282,14 +304,18 @@ export class ChatGPTAPI {
let response: Response let response: Response
try { try {
const url = `${this._apiBaseUrl}/auth/session`
const headers = { const headers = {
...this._headers, ...this._headers,
cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`, cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`,
accept: '*/*' accept: '*/*'
} }
console.log(`${this._apiBaseUrl}/auth/session`, headers)
const res = await fetch(`${this._apiBaseUrl}/auth/session`, { if (this._debug) {
console.log('GET', url, headers)
}
const res = await fetch(url, {
headers headers
}).then((r) => { }).then((r) => {
response = r response = r
...@@ -339,6 +365,10 @@ export class ChatGPTAPI { ...@@ -339,6 +365,10 @@ export class ChatGPTAPI {
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken) this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
return accessToken return accessToken
} catch (err: any) { } catch (err: any) {
if (this._debug) {
console.error(err)
}
const error = new types.ChatGPTError( const error = new types.ChatGPTError(
`ChatGPT failed to refresh auth token. ${err.toString()}` `ChatGPT failed to refresh auth token. ${err.toString()}`
) )
......
...@@ -273,10 +273,13 @@ export type MessageContent = { ...@@ -273,10 +273,13 @@ export type MessageContent = {
} }
export type MessageMetadata = any export type MessageMetadata = any
export type MessageActionType = 'next' | 'variant'
export type SendMessageOptions = { export type SendMessageOptions = {
conversationId?: string conversationId?: string
parentMessageId?: string parentMessageId?: string
messageId?: string
action?: MessageActionType
timeoutMs?: number timeoutMs?: number
onProgress?: (partialResponse: string) => void onProgress?: (partialResponse: string) => void
onConversationResponse?: (response: ConversationResponseEvent) => void onConversationResponse?: (response: ConversationResponseEvent) => void
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment