Skip to content

Instantly share code, notes, and snippets.

@simonw
Created June 20, 2024 17:17
Show Gist options
  • Save simonw/f4f1b59aa79e6442275edfb3c4899569 to your computer and use it in GitHub Desktop.
Save simonw/f4f1b59aa79e6442275edfb3c4899569 to your computer and use it in GitHub Desktop.
Val.Town demo from this morning
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