generated from vercel/ai-chatbot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added demo searcher api and tools for demo purposes
- Loading branch information
1 parent
087d638
commit e7401bb
Showing
8 changed files
with
202 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const SEARCHER_PROMPT = ` | ||
You are Discord Search Engine Use Only Demo Searcher Tool | ||
List Related Threads with their thread id. | ||
` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { kv } from '@vercel/kv' | ||
import { OpenAIStream, StreamingTextResponse } from 'ai' | ||
import { Ratelimit } from '@upstash/ratelimit' | ||
import OpenAI from 'openai' | ||
import { NextResponse } from 'next/server' | ||
|
||
import { SEARCHER_PROMPT } from './prompt' | ||
import { validateApiKey } from '@/lib/utils' | ||
import { demoSearchRunnable } from '@/lib/tools' | ||
|
||
export const runtime = 'edge' | ||
|
||
const discordToken = process.env.DISCORD_API_TOKEN as string | ||
|
||
async function authorization(token: string) { | ||
const validTokens = [discordToken]; | ||
|
||
if (!validTokens.includes(token)) { | ||
throw new Error('Unauthorized'); | ||
} | ||
} | ||
|
||
export async function POST(req: Request) { | ||
const json = await req.json() | ||
const { message, previewToken, authToken } = json | ||
|
||
let configuration | ||
let model | ||
|
||
try { | ||
await authorization(authToken) | ||
} catch (error) { | ||
return new Response(JSON.stringify({ error: 'Unauthorized' }), { | ||
status: 401, | ||
headers: { 'Content-Type': 'application/json' }, | ||
}); | ||
} | ||
|
||
const ip = req.headers.get('x-forwarded-for') | ||
|
||
if (validateApiKey(previewToken)) { | ||
configuration = { | ||
apiKey: previewToken | ||
} | ||
|
||
const ratelimit = new Ratelimit({ | ||
redis: kv, | ||
limiter: Ratelimit.slidingWindow(1000, '1d') | ||
}) | ||
|
||
const { success, limit, reset, remaining } = await ratelimit.limit( | ||
`ratelimit_${ip}` | ||
) | ||
|
||
if (!success) { | ||
return new Response('You have reached your request limit for the day.', { | ||
status: 429, | ||
headers: { | ||
'X-RateLimit-Limit': limit.toString(), | ||
'X-RateLimit-Remaining': remaining.toString(), | ||
'X-RateLimit-Reset': reset.toString() | ||
} | ||
}) | ||
} | ||
|
||
model = 'gpt-4-turbo' | ||
const openai = new OpenAI(configuration) | ||
|
||
const runner = openai.beta.chat.completions.runTools({ | ||
stream: true, | ||
model, | ||
temperature: 0.1, | ||
messages: [ | ||
{ | ||
role: 'system', | ||
content: SEARCHER_PROMPT | ||
}, | ||
{ | ||
role: 'user', | ||
content: message | ||
} | ||
], | ||
tools: demoSearchRunnable | ||
}) | ||
|
||
const stream = OpenAIStream(runner) | ||
return new StreamingTextResponse(stream) | ||
} else { | ||
return NextResponse.json( | ||
{ error: 'OPENAI API KEY NOT FOUND' }, | ||
{ status: 500 } | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import type { Tool } from './tool' | ||
import type { ChatCompletionCreateParams } from 'openai/resources/chat' | ||
import { getEmbeddings, getMatchesFromEmbeddings } from './utils' | ||
import { ScoredPineconeRecord } from '@pinecone-database/pinecone' | ||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction' | ||
|
||
export type Metadata = { | ||
url: string | ||
text: string | ||
chunk: string | ||
hash: string | ||
} | ||
|
||
const VECTOR_TYPE = 'demoSearch' | ||
|
||
const functionDescription: ChatCompletionCreateParams.Function = { | ||
name: 'demo_search_for_thread', | ||
description: | ||
'Search for context about discord threads', | ||
parameters: { | ||
type: 'object', | ||
properties: { | ||
query: { | ||
type: 'string', | ||
description: | ||
'The query to search for. 1-3 sentences are enough. English only.' | ||
} | ||
}, | ||
required: ['query'] | ||
} | ||
} | ||
|
||
const functionMessage = 'Fetching context about mina docs...\n' | ||
|
||
async function formatResults(matches: ScoredPineconeRecord[]) { | ||
const results = [] | ||
for (let i = 0; i < matches.length; i++) { | ||
const match = matches[i] | ||
if ((match.score || 1) > 0.25) { | ||
const metadata = match.metadata as Metadata | ||
const title = metadata.text | ||
const text = metadata.text | ||
const formatted_result = `## Result ${i + 1}:\n${title}\n${text}` | ||
results.push(formatted_result) | ||
} | ||
} | ||
return results.join('\n') | ||
} | ||
|
||
async function runTool(args: { query: string }): Promise<string> { | ||
try { | ||
const embeddings = await getEmbeddings(args.query) | ||
const matches = await getMatchesFromEmbeddings(embeddings, 15, VECTOR_TYPE) | ||
|
||
return formatResults(matches) | ||
} catch (e) { | ||
console.log('Error fetching docs: ', e) | ||
return 'Error fetching docs' | ||
} | ||
} | ||
|
||
export const demoSearchTool: Tool = { | ||
name: functionDescription.name, | ||
description: functionDescription, | ||
message: functionMessage, | ||
callable: runTool | ||
} | ||
|
||
export const demoSearchToolRunnable: RunnableToolFunction<{ query: string }> = { | ||
type: 'function', | ||
function: { | ||
name: functionDescription.name, | ||
function: runTool, | ||
parse: JSON.parse, | ||
description: | ||
'Search for context about discord threads', | ||
parameters: { | ||
type: 'object', | ||
properties: { | ||
query: { | ||
type: 'string', | ||
description: | ||
'The query to search for. 1-3 sentences are enough. English only.' | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters