Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c914a4f
Stub search functionality
bfollington Jan 21, 2025
422a87e
Stubbed search implementation
bfollington Jan 21, 2025
28d84b7
Implement three basic strategy ideas for search
bfollington Jan 21, 2025
2e99be5
Extract strategies and refactor logging
bfollington Jan 21, 2025
58bdd25
Extract LLM library functions
bfollington Jan 21, 2025
2261961
Factor out all redis code into lib
bfollington Jan 21, 2025
e5ed296
Format pass
bfollington Jan 21, 2025
505a93e
Introduce Logger interface
bfollington Jan 21, 2025
2c4174c
Improve docstrings
bfollington Jan 21, 2025
e8d5f21
Add types for redis
bfollington Jan 21, 2025
dc2d0a3
One more format pass
bfollington Jan 21, 2025
379f660
Default cors() settings clipper can reach blobby
bfollington Jan 21, 2025
f747da6
Fix lint errors
bfollington Jan 21, 2025
f3f7e19
Reverse refactor, use hono/client
bfollington Jan 22, 2025
21ad249
Restore state of world
bfollington Jan 22, 2025
cb5a676
Fix exported router types
bfollington Jan 22, 2025
360cffd
Get all effects working again with hono/client
bfollington Jan 22, 2025
dfa9fb8
Format pass
bfollington Jan 22, 2025
3684ab6
Fix lint
bfollington Jan 22, 2025
9a5d50a
adding a shared llm client wrapper that can be imported and used (#280)
jakedahn Jan 23, 2025
7d49f3f
Fix llm call
bfollington Jan 23, 2025
2c33e8a
Fixing errors so I can run the tests
bfollington Jan 23, 2025
3aa3aa5
Basic smoke tests for spell endpoints
bfollington Jan 23, 2025
b0b3095
Feels like this shouldn't work but it does
bfollington Jan 23, 2025
ef13ca1
Comment out tests
bfollington Jan 23, 2025
42e06ad
Add explaination
bfollington Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions typescript/packages/toolshed/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"tasks": {
"dev": "deno run -A --watch index.ts",
"dev": "deno run -A --watch --env-file=.env index.ts",
"test": "deno test -A --env-file=.env.test",
"build-lookslike": "deno run -A scripts/build-lookslike.ts"
},
Expand All @@ -18,7 +18,7 @@
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"],
"exclude": ["no-unused-vars"]
"exclude": ["no-unused-vars", "no-explicit-any"]
}
},
"nodeModulesDir": "auto",
Expand Down Expand Up @@ -55,6 +55,7 @@
"pino-pretty": "npm:pino-pretty@^13.0.0",
"redis": "npm:redis@^4.7.0",
"stoker": "npm:stoker@^1.4.2",
"zod": "npm:zod@^3.24.1"
"zod": "npm:zod@^3.24.1",
"mistreevous": "npm:mistreevous@4.2.0"
}
}
11 changes: 11 additions & 0 deletions typescript/packages/toolshed/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions typescript/packages/toolshed/lib/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { State } from "mistreevous";
import { Logger, PrefixedLogger } from "./prefixed-logger.ts";

// Handles logging and instrumentation
export abstract class BaseAgent {
protected logger: PrefixedLogger;
protected agentName: string;
protected stepDurations: Record<string, number> = {};
protected logs: string[] = [];
protected startTime: number = 0;

constructor(logger: Logger, agentName: string) {
this.agentName = agentName;
this.logger = new PrefixedLogger(logger, agentName);
this.startTime = Date.now();
}

protected async measureStep(
stepName: string,
fn: () => Promise<State>,
): Promise<State> {
const start = Date.now();
const result = await fn();
this.stepDurations[stepName] = Date.now() - start;
this.logger.info(
`${this.agentName}: ${stepName} took ${this.stepDurations[stepName]}ms`,
);
return result;
}

protected getMetadata() {
const totalDuration = Date.now() - this.startTime;
return {
totalDuration,
stepDurations: this.stepDurations,
logs: this.logs,
};
}
}
46 changes: 46 additions & 0 deletions typescript/packages/toolshed/lib/llm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { AppType } from "@/app.ts";
import { hc } from "hono/client";

// NOTE(jake): Ideally this would be exposed via the hono client, but I wasn't
// able to get it all wired up. Importing the route definition is fine for now.
import type { GetModelsRouteQueryParams } from "@/routes/ai/llm/llm.routes.ts";

const client = hc<AppType>("http://localhost:8000/");

export async function listAvailableModels({
capability,
task,
search,
}: GetModelsRouteQueryParams) {
const res = await client.api.ai.llm.models.$get({
query: {
search,
capability,
task,
},
});
return res.json();
}

export async function generateText(
query: Parameters<typeof client.api.ai.llm.$post>[0]["json"],
): Promise<string> {
const res = await client.api.ai.llm.$post({ json: query });
const data = await res.json();

if ("error" in data) {
throw new Error(data.error);
}

if ("type" in data && data.type === "json") {
return data.body.content;
}

if ("content" in data) {
// bf: this is actually the case that runs, even if the types disagree
// no idea why
return (data as any).content;
}

throw new Error("Unexpected response from LLM server");
}
57 changes: 57 additions & 0 deletions typescript/packages/toolshed/lib/prefixed-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export interface Logger {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads up, I already have some fancy logging stuff using pino-logger https://github.com/pinojs/pino

It's currently only hooked up to hono via a middleware, and not super obvious how to use elsewhere: https://github.com/commontoolsinc/labs/blob/main/typescript/packages/toolshed/middlewares/pino-logger.ts

I'm cool merging this as-is, but I'll probably make a follow-up pass at killing pino or this setup in the next week or two.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM! I would prefer to use a library, I was just losing my sanity without context in the logs. Let's rip it out soon 👍

// deno-lint-ignore no-explicit-any
info(...args: any[]): void;
// deno-lint-ignore no-explicit-any
error(...args: any[]): void;
// deno-lint-ignore no-explicit-any
warn(...args: any[]): void;
// deno-lint-ignore no-explicit-any
debug(...args: any[]): void;
}

export class PrefixedLogger implements Logger {
private logger: Logger;
private prefix: string;
private logMessages: string[] = [];

constructor(logger: Logger = console, prefix: string) {
this.logger = logger;
this.prefix = prefix;
this.info = this.info.bind(this);
this.error = this.error.bind(this);
this.warn = this.warn.bind(this);
this.debug = this.debug.bind(this);
}

// deno-lint-ignore no-explicit-any
info(...args: any[]) {
const message = [`[${this.prefix}]`, ...args].join(" ");
this.logMessages.push(message);
this.logger.info(message);
}

// deno-lint-ignore no-explicit-any
error(...args: any[]) {
const message = [`[${this.prefix}]`, ...args].join(" ");
this.logMessages.push(message);
this.logger.error(message);
}

// deno-lint-ignore no-explicit-any
warn(...args: any[]) {
const message = [`[${this.prefix}]`, ...args].join(" ");
this.logMessages.push(message);
this.logger.warn(message);
}

// deno-lint-ignore no-explicit-any
debug(...args: any[]) {
const message = [`[${this.prefix}]`, ...args].join(" ");
this.logMessages.push(message);
this.logger.debug(message);
}

getLogs(): string[] {
return this.logMessages;
}
}
17 changes: 17 additions & 0 deletions typescript/packages/toolshed/lib/response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export async function handleResponse<T>(response: Response): Promise<T> {
const data = await response.json();

if ("error" in data) {
throw new Error(data.error);
}

if (data.type === "json") {
return data.body;
}

if (data instanceof Response) {
throw new Error(data.statusText);
}

return null as never;
}
46 changes: 46 additions & 0 deletions typescript/packages/toolshed/lib/schema-match.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Schema, Validator } from "jsonschema";

export function checkSchemaMatch(
data: Record<string, unknown>,
schema: Schema,
): boolean {
const validator = new Validator();

const jsonSchema: unknown = {
type: "object",
properties: Object.keys(schema).reduce(
(acc: Record<string, unknown>, key) => {
const schemaValue = schema[key as keyof Schema];
acc[key] = { type: (schemaValue as any)?.type || typeof schemaValue };
return acc;
},
{},
),
required: Object.keys(schema),
additionalProperties: true,
};

const rootResult = validator.validate(data, jsonSchema as Schema);
if (rootResult.valid) {
return true;
}

function checkSubtrees(obj: unknown): boolean {
if (typeof obj !== "object" || obj === null) {
return false;
}

if (Array.isArray(obj)) {
return obj.some((item) => checkSubtrees(item));
}

const result = validator.validate(obj, jsonSchema as Schema);
if (result.valid) {
return true;
}

return Object.values(obj).some((value) => checkSubtrees(value));
}

return checkSubtrees(data);
}
4 changes: 2 additions & 2 deletions typescript/packages/toolshed/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { OpenAPIHono, RouteConfig, RouteHandler } from "@hono/zod-openapi";
import type { PinoLogger } from "hono-pino";
import type { Logger } from "pino";
import type { RedisClientType } from "redis";

export interface AppBindings {
Variables: {
logger: PinoLogger;
logger: Logger;
blobbyRedis: RedisClientType;
};
}
Expand Down
4 changes: 3 additions & 1 deletion typescript/packages/toolshed/routes/ai/llm/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export async function loadItem(key: string): Promise<CacheItem | null> {
const cacheData = await Deno.readTextFile(filePath);
console.log(
`${timestamp()} ${colors.green}📦 Cache loaded:${colors.reset} ${
filePath.slice(-12)
filePath.slice(
-12,
)
}`,
);
return JSON.parse(cacheData);
Expand Down
Loading