Created
June 20, 2024 17:17
-
-
Save simonw/f4f1b59aa79e6442275edfb3c4899569 to your computer and use it in GitHub Desktop.
Val.Town demo from this morning
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
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth?v=65"; | |
import Anthropic from "npm:@anthropic-ai/[email protected]"; | |
const anthropic = new Anthropic(); | |
async function suggestKeywords(question) { | |
const message = await anthropic.messages.create({ | |
max_tokens: 128, | |
model: "claude-3-5-sonnet-20240620", | |
tools: [{ | |
name: "suggested_search_keywords", | |
description: "Suggest individual search keywords to help answer the question.", | |
input_schema: { | |
type: "object", | |
properties: { | |
keywords: { | |
type: "array", | |
items: { | |
type: "string", | |
}, | |
description: "List of suggested single word search keywords", | |
}, | |
}, | |
required: ["keywords"], | |
}, | |
}], | |
tool_choice: { type: "tool", name: "suggested_search_keywords" }, | |
messages: [ | |
{ role: "user", content: question }, | |
], | |
}); | |
if (message.content[0].type == "text") { | |
throw new Error(message.content[0].text); | |
} | |
return message.content[0].input.keywords; | |
} | |
const keywords = [ | |
"shot-scraper", | |
"screenshot", | |
"web", | |
"tool", | |
"automation", | |
"CLI", | |
]; | |
const sql = `select | |
blog_entry.id, | |
blog_entry.title, | |
blog_entry.body, | |
blog_entry.created | |
from | |
blog_entry | |
join blog_entry_fts on blog_entry_fts.rowid = blog_entry.rowid | |
where | |
blog_entry_fts match :search | |
order by | |
rank | |
limit | |
10`; | |
async function runSearch(keywords) { | |
const search = keywords.map(s => `"${s}"`).join(" OR "); | |
const params = new URLSearchParams({ | |
search, | |
sql, | |
_shape: "array", | |
}); | |
const url = "https://datasette.simonwillison.net/simonwillisonblog.json?" + params; | |
const result = await (await fetch(url)).json(); | |
return result; | |
} | |
export default basicAuth(async function(req: Request) { | |
const url = new URL(req.url); | |
const question = url.searchParams.get("question").slice(0, 40); | |
if (!question) { | |
return Response.json({ "error": "No question provided" }); | |
} | |
// Turn the question into search terms | |
const keywords = await suggestKeywords(question); | |
// Run the actual search | |
const result = await runSearch(keywords); | |
// Strip HTML tags from each body property, modify in-place: | |
result.forEach(r => { | |
r.body = r.body.replace(/<[^>]*>/g, ""); | |
}); | |
// Glue togethera. string of the title and body properties in one go | |
const context = result.map(r => r.title + " " + r.body).join("\n\n"); | |
const message = await anthropic.messages.create({ | |
max_tokens: 1024, | |
model: "claude-3-5-sonnet-20240620", // "claude-3-haiku-20240307", | |
system: "Return a full HTML document as your answer, no markdown, make it pretty with exciting relevant CSS", | |
messages: [ | |
{ role: "user", content: context }, | |
{ role: "assistant", content: "Thank you for the context, I am ready to answer your question as HTML" }, | |
{ role: "user", content: question }, | |
], | |
}); | |
return new Response(message.content[0].text, { status: 200, headers: { "Content-Type": "text/html" } }); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment