Skip to content

Instantly share code, notes, and snippets.

@awakenedhaggis
Created December 4, 2023 23:32
Show Gist options
  • Save awakenedhaggis/bd9dbf2421325117f7e5c20f62e1c99f to your computer and use it in GitHub Desktop.
Save awakenedhaggis/bd9dbf2421325117f7e5c20f62e1c99f to your computer and use it in GitHub Desktop.
// Name: Kagi FastGPT
// Description: Query FastGPT by Kagi
// Author: Phil Duncan
import '@johnlindquist/kit';
/** A full {@link https://help.kagi.com/kagi/api/fastgpt.html#fastgpt-answer | FastGPT Answer} object */
interface Answer {
meta: Meta;
data: Data;
}
interface Data {
// LLM response
output: string;
// Number of tokens used
tokens: number;
// References used in output, in order
references: Reference[];
}
interface Meta {
id: string;
// Location of server
node: string;
// Time in ms for a response
ms: number;
// Remaining balance
api_balance: number;
}
interface Reference {
title: string;
// Short text from reference
snippet: string;
url: string;
}
/**
* Additional non-required
* {@link https://help.kagi.com/kagi/api/fastgpt.html#parameters | Parameters}
*
* @requires query
*/
interface Parameters {
/** Main query sent to FastGPT */
query: string;
/** Whether to allow cached requests & responses. (default true) */
cache?: boolean;
/** @deprecated Parameter is out of service, and may be removed */
web_search?: boolean;
}
let url = 'https://kagi.com/api/v0/fastgpt';
const key = await env('KAGI_API_KEY', {
hint: `Generate your <a href=https://kagi.com/settings?p=api>api key</a>`,
secret: true,
shortcuts: [
{
name: 'Generate Key at Kagi.com',
key: `${cmd}+g`,
bar: 'left',
onPress: () => {
open('https://kagi.com/settings?p=api');
},
},
],
});
let lastQuery: string;
let currentAnswer: Answer;
async function callFastGPT(params: Parameters): Promise<Answer> {
const request = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bot ${key}`,
},
body: JSON.stringify({
...params,
}),
});
if (!request.ok) handleError(request.statusText, 'RequestApi');
const response: Answer = await request.json();
return response;
}
async function addToChat(answer: Answer) {
//Replace loading text with reponse
chat.setMessage(-1, md(currentAnswer.data.output));
// List all references
currentAnswer.data.references.forEach((reference, index) => {
chat.addMessage(`<a href="${reference.url}">[${index + 1}] ${reference.title}</a>`);
});
}
function handleError(message: string, initiator?: string) {
const errorPrefix = initiator ? `[${initiator}]` : '[Error]';
warn(errorPrefix, message);
}
await chat({
shortcuts: [
{
name: 'Close',
key: `${cmd}+w`,
onPress: () => {
process.exit();
},
bar: 'left',
},
{
name: 'Regenerate Answer',
key: `${cmd}+r`,
onPress: async () => {
if (!lastQuery) return handleError('No query to regenerate');
chat.addMessage('...');
const newAnswer = await callFastGPT({
query: lastQuery,
cache: false,
});
addToChat(newAnswer);
},
bar: 'left',
},
],
onSubmit: async (query) => {
lastQuery = query;
chat.addMessage('...');
currentAnswer = await callFastGPT({
query,
});
addToChat(currentAnswer);
setHint(
`Remaining Balance: ${currentAnswer.meta.api_balance} <a href="https://kagi.com/settings?p=billing_api">top up?</a>`,
);
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment