Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry Fontanier committed Feb 18, 2025
1 parent b33960e commit 1eeefa1
Show file tree
Hide file tree
Showing 11 changed files with 561 additions and 165 deletions.
5 changes: 5 additions & 0 deletions connectors/henry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
async function main() {
//
}

main().catch(console.error);
116 changes: 0 additions & 116 deletions x/henry/mp-sandbox-agent/agent.ts

This file was deleted.

109 changes: 109 additions & 0 deletions x/henry/mp-sandbox-agent/agent/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { describe, expect, it } from "bun:test";
import { z } from "zod";
import { generateToolDocs } from "./helpers";
import { defineTool } from "../tools/helpers";
import { ToolOutput } from "../tools/types";

describe("generateToolDocs", () => {
it("should generate docs for a simple tool", () => {
const simpleTool = defineTool(
"A simple test function",
z.object({
name: z.string().describe("The name parameter"),
}),
z.string().describe("The return value"),
async () => ({ type: "success", result: "test" })
);

const docs = generateToolDocs({ simpleTool });
expect(docs).toContain("Available functions:");
expect(docs).toContain(
"All functions may return None if they fail (check for None before accessing the result)."
);
expect(docs).toContain(
"simpleTool(name): async function - A simple test function"
);
expect(docs).toContain("* name: The name parameter");
expect(docs).toContain("Returns:");
expect(docs).toContain("The return value");
});

it("should generate docs for a tool with complex types", () => {
const complexTool = defineTool(
"A complex test function",
z.object({
user: z
.object({
name: z.string().describe("User's name"),
age: z.number().describe("User's age"),
})
.describe("User object"),
options: z.array(z.string()).describe("List of options"),
}),
z.object({
id: z.number().describe("User ID"),
settings: z
.array(
z.object({
key: z.string().describe("Setting key"),
value: z.string().describe("Setting value"),
})
)
.describe("User settings"),
}),
async () => ({ type: "success", result: { id: 1, settings: [] } })
);

const docs = generateToolDocs({ complexTool });
expect(docs).toContain(
"complexTool(user, options): async function - A complex test function"
);
expect(docs).toContain("* user: dictionary with keys:");
expect(docs).toContain(" * name: User's name");
expect(docs).toContain(" * age: User's age");
expect(docs).toContain("* options: array of string");
expect(docs).toContain("Returns:");
expect(docs).toContain("dictionary with keys:");
expect(docs).toContain("* id: User ID");
expect(docs).toContain("* settings: array of dictionary with keys:");
expect(docs).toContain(" * key: Setting key");
expect(docs).toContain(" * value: Setting value");
});

it("should handle multiple tools", () => {
const tool1 = defineTool(
"First tool",
z.object({ a: z.string() }),
z.number(),
async () => ({ type: "success", result: 1 })
);

const tool2 = defineTool(
"Second tool",
z.object({ b: z.boolean() }),
z.string(),
async () => ({ type: "success", result: "test" })
);

const docs = generateToolDocs({ tool1, tool2 });
expect(docs).toContain("tool1(a): async function - First tool");
expect(docs).toContain("tool2(b): async function - Second tool");
});

it("should handle tools with nested output types", () => {
const outputTool = defineTool(
"Output test function",
z.object({ input: z.string() }),
z.string(),
async () => ({ type: "success", result: "test" })
);

const docs = generateToolDocs({ outputTool });
// Should only show the success case type
expect(docs).toContain("Returns:");
expect(docs).toContain("string");
// Should not show the discriminated union structure
expect(docs).not.toContain("type:");
expect(docs).not.toContain("result:");
});
});
71 changes: 71 additions & 0 deletions x/henry/mp-sandbox-agent/agent/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { Tool } from "../tools/types";
import { z } from "zod";

function describeZodType(schema: z.ZodType, indent: string = ""): string {
if (schema instanceof z.ZodArray) {
return `array of ${describeZodType(schema.element, indent + " ")}`;
} else if (schema instanceof z.ZodObject) {
let desc = "dictionary with keys:\n";
for (const [fieldName, fieldSchema] of Object.entries(schema.shape)) {
desc += `${indent} * ${fieldName}: ${describeZodType(
fieldSchema as z.ZodType,
indent + " "
)
.split("\n")
.join("\n" + indent)}\n`;
}
return desc;
} else if (schema instanceof z.ZodUnion && schema.options.length === 2) {
// Check if this is a ToolOutput schema
const successCase = schema.options.find(
(opt: z.ZodType) =>
opt instanceof z.ZodObject && opt.shape.type?.value === "success"
) as z.ZodObject<any> | undefined;

if (successCase?.shape.result) {
return describeZodType(successCase.shape.result, indent);
}
// If we can't handle this union type, just describe it as a union
return `union of ${schema.options
.map((opt: z.ZodType) => describeZodType(opt, indent + " "))
.join(" | ")}`;
} else {
return (
schema.description ||
schema.constructor.name.replace("Zod", "").toLowerCase() ||
"any"
);
}
}

export function generateToolDocs(tools: Record<string, Tool>): string {
let docs = "Available functions:\n";
docs +=
"Note: All functions may return None if they fail (check for None before accessing the result).\n\n";

for (const [fnName, { description, input, output }] of Object.entries(
tools
)) {
// Function signature with description
const inputObject = input as z.ZodObject<any>;

docs += `- ${fnName}(${Object.keys(inputObject.shape).join(
", "
)}): async function - ${description}\n`;

// Input parameters
docs += " Parameters:\n";
for (const [paramName, paramSchema] of Object.entries(inputObject.shape)) {
docs += ` * ${paramName}: ${describeZodType(
paramSchema as z.ZodType,
" "
)}\n`;
}

// Output fields
docs += " Returns:\n";
docs += describeZodType(output, " ");
}

return docs;
}
Loading

0 comments on commit 1eeefa1

Please sign in to comment.