export type ModelAuthor = 'google' | 'openai' export type Model = 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo' | 'gpt-4' | 'gpt-4-turbo-preview' | 'gemini-1.0-pro' | 'gemini-1.5-pro-latest' export type Role = 'system' | 'user' | 'assistant' | 'guidance' export type MessagePart = | { text: string } | { imageURL: string, imageDetail?} | { imageB64Url: string, imageDetail?} | { data: Buffer, mimeType?: string, imageDetail? } export type Message = | { role: Role, text: string } | { role: Role, parts: MessagePart[] } export type ChunkCb = ({ n: number, textDelta: string, parts: MessagePart, done: boolean }) => void export type FnCall = { id?: number, name: string, args: Record } // TODO: Turn FnCalls from Record to FnCall[] (breaking) export type FnCalls = FnCall[] export type Usage = { inputTokens: number, outputTokens: number, totalTokens: number, cachedInputTokens?: number } export type CompletionResponse = { type: 'text' | 'function', parts: MessagePart[], text?: string, fnCalls?: FnCalls, requestUsage?: Usage } declare module 'langxlang' { type CompletionOptions = { maxTokens?: number stopSequences?: string[] temperature?: number topP?: number topK?: number } type TranscriptionOptions = { temperature?: number, responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt', granularity?: 'word' | 'sentence', } class CompletionService { // Creates an instance of completion service. // Note: as an alternative to explicitly passing the API keys in the constructor you can: // * set the `OPENAI_API_KEY` and `GEMINI_API_KEY` environment variables. // * or, define the keys inside `/.local/share/lxl-cache.json` (linux), `~/Library/Application Support/lxl-cache.json` (mac), or `%appdata%\lxl-cache.json` (windows) with the structure // `{"keys": {"openai": "your-openai-key", "gemini": "your-gemini-key"}}` // In this options object, you can specify generation options that will be applied by default to // all requestCompletion calls. You can override these options by passing them in the requestCompletion call. constructor(apiKeys: { [k in ModelAuthor]?: string }, options?: { apiBase?: string, generationOptions?: CompletionOptions }) cachePath: string // Start logging LLM requests startLogging(): boolean // Stop logging LLM requests, return the HTML of the log stopLogging(): { exportHTML: () => string } listModels(): Promise> countTokens(author: ModelAuthor, model: Model, text: string | MessagePart[]): Promise countTokensInMessages(author: ModelAuthor, model: Model, text: string | MessagePart[]): Promise // Request a completion from the model with a system prompt and a single user prompt. requestCompletion(author: ModelAuthor | string, model: Model | string, parts: string | MessagePart[], _chunkCb?: ChunkCb, options?: CompletionOptions & { // If true, the response will be cached and returned from the cache if the same request is made again. enableCaching?: boolean }): Promise // Request a completion from the model with a sequence of chat messages which have roles. requestChatCompletion(author: ModelAuthor | string, model: Model | string, options: { messages: Message[], generationOptions?: CompletionOptions }, chunkCb?: ChunkCb): Promise requestTranscription(author: ModelAuthor | string, model: Model | string, audioStream: FsReadStream | Blob | Buffer, format: 'json', options?: TranscriptionOptions): Promise<{ text: string }> requestTranscription(author: ModelAuthor | string, model: Model | string, audioStream: FsReadStream | Blob | Buffer, format: 'verbose_json', options?: TranscriptionOptions): Promise<{ language: string, text: string, segments?: { start: number, end: number, word: string }[], words?: { start: number, end: number, word: string }[] }> requestSpeechSynthesis (author: ModelAuthor | string, model: Model | string, text: string, options?: { voice?: string, speed?: number, pitch?: number, volume?: number }): Promise } // Note: GoogleAIStudioCompletionService does NOT use the official AI Studio API, but instead uses a relay server to forward requests to an AIStudio client. // Use the standard CompletionService instead to use the official API. class GoogleAIStudioCompletionService { // Creates an instance of GoogleAIStudioCompletionService that hosts a WebSocket server at specified port. // AIStudio clients can connect to that port to work with LXL. // The port is the port that the server should listen on. constructor(port: number) // Creates an instance that instead makes an HTTP request to a relay server, // that then forwards the request to an AIStudio client. constructor(args: { baseURL: string, apiKey: string }) // Promise that resolves when the server is ready to accept requests. ready: Promise // Stop the server. stop(): void // Stop the server. Same as stop(). close(): void // Request a non-streaming completion from the model. requestCompletion(author: '', model: Model, text: string, chunkCb?: ChunkCb, options?: CompletionOptions & { autoFeed?: { // Once a line matching stopLine is hit, stop trying to feed the model more input stopLine: string, // The maximum number of rounds to feed the model maxRounds: number, }, // If true, the response will be cached and returned from the cache if the same request is made again. enableCaching?: boolean }): Promise // Request a completion from the model with a sequence of chat messages which have roles. requestChatCompletion(author: '', model: Model, options: { messages: Message[], generationOptions: CompletionOptions }, chunkCb: ChunkCb): Promise } type SomeCompletionService = CompletionService | GoogleAIStudioCompletionService interface Func { // If default is not provided, the argument is required. Arg(options: { type: string[], description: string, example?: string, default?: any, required?: boolean }): string Arg(options: { type: object, description: string, example?: string, default?: any, required?: boolean }): object Arg(options: { type: T, description: string, example?: string, default?: any, required?: boolean }): T Desc(description: string): void } // The functions that can be used in the user prompt. type ModelFn = ((input: any) => any) & { description: string, parameters?: Record } type Functions = Record class ChatSession/**/ { // ChatSession is for back and forth conversation between a user an an LLM. constructor( completionService: SomeCompletionService, author: ModelAuthor | string, model: Model | string, systemPrompt?: string, options?: { generationOptions: CompletionOptions, functions?: Functions } ) // constructor(completionService: SomeCompletionService, author: ModelAuthor | string, model: Model | string, systemPrompt?: string, options?: { functions?: Functions, generationOptions?: CompletionOptions }) // Send a message to the LLM and receive a response as return value. The chunkCallback // can be defined to listen to bits of the message stream as it's being written by the LLM. sendMessage(userMessage: string | MessagePart[], chunkCallback?: ChunkCb, generationOptions?: CompletionOptions & { endOnFnCall?: boolean }): Promise<{ parts: MessagePart[], text?: string, calledFunctions: FnCalls[], usage?: Usage }> setFunctions(functions: Functions): void setAndSendMessages( messages: Message[], chunkCallback?: ChunkCb, generationOptions?: CompletionOptions & { endOnFnCall?: boolean } ): Promise<{ parts: MessagePart[], text?: string, calledFunctions: FnCall[][], endReason: 'text' | 'function', usage?: Usage }> } // ============ TOOLS ============ type StripOptions = { stripEmailQuotes?: boolean, replacements?: Map, allowMalformed?: boolean } interface CollectFolderOptions { // What extension/extension(s) of files in the repo to include extension?: string | string[] // Either a function that returns true if the file should be included, or false if not (otherwise the call result is ignored). // OR alternatively, pass an array of regexes of which one needs to match for inclusion. matching?: ((relativePath: string, absolutePath: string, wouldBeExcluded: boolean) => boolean) | RegExp[] // An optional list of strings for which if the path starts with one of them, it's excluded, even if it was matched by `extension` or `matching` // unless `matching` was a function and it explicitly returned `true` for the file. excluding?: Array // Try and cut down on the token size of the input by doing "stripping" to remove semantically unnecessary tokens from file strip?: StripOptions // Truncate large files to this many GPT-4 tokens truncateLargeFiles?: number // Include binary files in the output. Binary files (<90% ASCII chars) can't be represented as text typically, // so default is to exclude them. includeBinaryFiles?: boolean } interface Tools { // Generate HTML that shows side-by-side outputs for the system/user prompt across different models. makeVizForPrompt(systemPrompt: string, userPrompt: string, models: Model[], options?: { title?: string, description?: string, aiStudioPort?: number }): Promise // Returns a JS object with a list of files in a folder collectFolderFiles(folderPath: string, options: CollectFolderOptions): Promise<[absolutePath: string, relativePath: string, contents: string][]> // Returns a JS object with a list of files in a GitHub repo collectGithubRepoFiles(repo: string, options: CollectFolderOptions & { // The branch or ref to use branch?: string, // The URL to the repo, if it's not github.com url?: string, // The token to use for authentication, if the repo is private token?: string }): Promise<[absolutePath: string, relativePath: string, contents: string][]> // Takes output from collectFolderFiles or collectGithubRepoFiles and returns a markdown string from it concatFilesToMarkdown(files: [absolutePath: string, relativePath: string, contents: string][], options?: { // Disable if markdown code blocks should have a language tag (e.g. ```python) noCodeblockType: boolean }): string // Returns a function that can be passed to chunkCb in ChatSession.sendMessage, but with // a type writer effect. This can be helpful for GoogleAIStudioCompletionService, as it // gives big chunks over long periods of time unlike OpenAI APIs. Default pipeTo is process.stdout. createTypeWriterEffectStream(pipeTo?: NodeJS.WritableStream): (chunk) => void // Pre-processes markdown and replaces variables and conditionals with data from `vars` loadPrompt(text: string, vars: Record, opts: { roles: Record }): Message[] loadPrompt(text: string, vars: Record): string // Loads a file from disk (from current script's relative path or absolute path) and // replaces variables and conditionals with data from `vars` importPromptSync(filePath: string, vars: Record, opts: { roles: Record }): Message[] importPromptSync(filePath: string, vars: Record): string // Loads a file from disk (from current script's relative path or absolute path) and // replaces variables and conditionals with data from `vars` importPrompt(filePath: string, vars: Record, opts: { roles: Record }): Promise importPrompt(filePath: string, vars: Record): Promise // Reads a file from disk and returns the raw contents importRawSync(filePath: string): string // Takes in a prompt string and splits it by `roles` into an array of messages that are segmented by role. // The `roles` arg is a record of sequences to role. Default roles are <|SYSTEM|> (system), <|USER|> (user), and <|ASSISTANT|> (assistant). segmentPromptByRoles(prompt: string, roles?: Record): { role: Role, content: string }[] markdown: { addLineNumbers(input: string, minLineNumWidth): string removeLineNumbers(input: string): string } // Various string manipulation tools to minify/strip down strings stripping: { stripMarkdown(input: string, options?: StripOptions): string // Normalize line endings to \n normalizeLineEndings(str: string): string // Remove unnecessary keywords from a string stripJava(input: string, options?: StripOptions & { removeStrings?: boolean }): string // Removes files from git diff matching the options.excluding regexes stripDiff(input: string, options?: { excluding: RegExp[] }): string } // Extracts code blocks from markdown extractCodeblockFromMarkdown(markdownInput: string): { raw: string, lang: string, code: string }[] // Extracts function calls from a string that are wrapped in a specific enclosing sequence (like `some_method("some_arg", 1, [2])`). // Note if you use a stop sequence after a fn call, don't pass a closing sequence as may not be present. extractJSFunctionCall(str: string, enclosing?: string, closing?: string): { name: string, args: string[] } // Wraps the contents by using the specified token character at least 3 times, // ensuring that the token is long enough that it's not present in the content wrapContent(content: string, withChar?: string, initialTokenSuffix?: string): string } const tools: Tools const Func: Func // Flow is a class that can be used to create a chain of prompts and response handling. // You can also ask follow up questions somewhere along the chain, and Flow will stop // executing the chain and wait for the follow up to be answered. // This is different from ChatSession, which is for back and forth conversation, and // intended for more advanced uses where you want to change the dialogue between a session. class Flow { // The responses for the last time the flow was run. Helpful for recovering from errors. lastResponses: CompletionResponse[] lastFlow: SomeFlowChainObject lastRunParameters: Record constructor(completionService: CompletionService, chain: RootFlowChain, options: { model: Model }) run(parameters?: Record): Promise followUp(priorRun: FlowRun, name: string, parameters?: Record): Promise } export class SafetyError extends Error { constructor(message: string) } } // FLOW interface FlowChainObjectBase { model: Model prompt: | { system: string, user: string } | { text: string, roles: Record } with: Record followUps: Record SomeFlowChainObject> // The function that transforms the response from the model before it's passed to the followUps/next or returned transformResponse: (response: CompletionResponse) => object } interface FlowChainObject extends FlowChainObjectBase { next: (response: CompletionResponse) => SomeFlowChainObject } interface FlowChainObjectOneOf extends FlowChainObjectBase { nextOneOf: (response: CompletionResponse) => Record discriminator: (response: CompletionResponse) => string } type SomeFlowChainObject = FlowChainObject | FlowChainObjectOneOf type RootFlowChain = (parameters: Record) => SomeFlowChainObject type FlowRunResponse = CompletionResponse & { name?: string } type FlowRun = { // The final response from the flow response: FlowRunResponse, // The responses from each step in the flow responses: FlowRunResponse[] }