-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add real TypeChain Petstore agent factory #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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,63 @@ | ||
| # Real TypeChain Petstore Agent Implementation Plan | ||
|
|
||
| > **For Hermes:** Implement this issue-scoped plan with test-first slices and preserve the `dev` → release-only `main` workflow. | ||
|
|
||
| **Goal:** Add a real LangChain agent factory that uses TypeChain `createTypeMcpAgent()` to operate the existing read-only Swagger Petstore TypeMCP tools, while keeping provider credentials and runtime selection application-owned. | ||
|
|
||
| **Architecture:** Create a narrowly scoped `createPetstoreAgent()` factory. It accepts a caller-provided LangChain chat model and an optional resolver-backed `PetstoreServer`, then delegates agent construction to TypeChain's `createTypeMcpAgent()`. A test uses LangChain's `FakeToolCallingModel` plus the existing deterministic fixture server to prove an actual agent loop selects and invokes `search_available_pets`; no network or model provider runs in CI. A separate manual composition example accepts a model from the caller rather than initializing a provider or reading a secret. | ||
|
|
||
| **Tech stack:** TypeScript, Vitest, LangChain `FakeToolCallingModel`, `@theorvane/type-chain/typemcp`, TypeMCP decorators, Zod. | ||
|
|
||
| --- | ||
|
|
||
| ### Task 1: Define and prove the real agent factory contract | ||
|
|
||
| **Files:** | ||
| - Create: `test/typechain-petstore-real-agent.test.ts` | ||
| - Create: `examples/typechain-petstore-real-agent.ts` | ||
|
|
||
| 1. Write a failing integration test importing `createPetstoreAgent()`. | ||
| 2. Use `FakeToolCallingModel` to emit one `search_available_pets` call with `{ limit: 2 }`, followed by an empty tool-call response. | ||
| 3. Invoke the constructed agent with a user message and assert it includes a LangChain tool message for that call containing the fixture pet result. | ||
| 4. Run the focused test and observe failure because the module/export does not exist. | ||
| 5. Implement only the factory: accept `{ model, server? }`, default to a `PetstoreServer`, supply its explicit resolver to `createTypeMcpAgent()`, and return the LangChain agent. | ||
| 6. Re-run the focused test, then lint and typecheck. | ||
|
|
||
| ### Task 2: Add executable fixture agent-loop evidence | ||
|
|
||
| **Files:** | ||
| - Create: `examples/typechain-petstore-real-agent-fixture.ts` | ||
| - Modify: `package.json` | ||
| - Modify: `test/typechain-petstore-real-agent.test.ts` | ||
|
|
||
| 1. Write a failing assertion for a runnable fixture command that reports the agent's selected tool and its tool result. | ||
| 2. Implement the fixture runner with `FakeToolCallingModel` and the existing `createFixturePetstoreServer()`. | ||
| 3. Add `example:petstore:agent:real:fixture`; do not add a provider package or live credential-dependent command. | ||
| 4. Run the focused test and fixture command successfully. | ||
|
|
||
| ### Task 3: Document real-agent composition without adding provider ownership | ||
|
|
||
| **Files:** | ||
| - Modify: `README.md` | ||
| - Modify: `docs/planning/2026-07-29-real-typechain-petstore-agent.md` | ||
|
|
||
| 1. Document the distinction between the deterministic adapter workflow and the real `createTypeMcpAgent()` factory. | ||
| 2. Show a short application-owned pseudo-composition snippet with a `model` supplied by the caller. Do not use an API key, `process.env`, or provider-specific package. | ||
| 3. State that TypeChain constructs the in-process LangChain agent but does not select/configure a provider, host an MCP transport, create an MCP client/session, or authorize tool invocations. | ||
| 4. Run formatting, full checks, audit, and whitespace checks. | ||
|
|
||
| ### Task 4: Deliver through governed PRs | ||
|
|
||
| 1. Commit the plan and implementation in focused commits. | ||
| 2. Create a `dev` PR closing #21. | ||
| 3. Obtain exact-head independent review, green `verify`, and resolved threads before squash merge. | ||
| 4. Reconcile `main` ancestry into `dev` only if required by current remote history, via a two-parent merge with separate review. | ||
| 5. Create a reviewed `dev` → `main` promotion; require exact-head `verify`, `release-promotion`, independent approval, and resolved threads before squash merge. | ||
| 6. Clone current `main` cleanly and run `npm ci`, `npm run check`, and the real-agent fixture command. | ||
|
|
||
| ## Explicit non-goals | ||
|
|
||
| - No provider SDK, API key, model identifier, or environment-variable model configuration. | ||
| - No automated live LLM invocation in CI. | ||
| - No MCP HTTP/stdio server, client/session, or cross-process transport. | ||
| - No Petstore write endpoint or credential path. |
This file contains hidden or 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,51 @@ | ||
| import { ToolMessage } from "@langchain/core/messages"; | ||
| import { FakeToolCallingModel } from "langchain"; | ||
|
|
||
| import { createFixturePetstoreServer } from "./petstore-fixture.js"; | ||
| import { createPetstoreAgent } from "./typechain-petstore-real-agent.js"; | ||
|
|
||
| export async function run(): Promise<void> { | ||
| const agent = await createPetstoreAgent({ | ||
| model: new FakeToolCallingModel({ | ||
| toolCalls: [ | ||
| [ | ||
| { | ||
| id: "available-pets-call", | ||
| name: "search_available_pets", | ||
| args: { limit: 2 }, | ||
| }, | ||
| ], | ||
| [], | ||
| ], | ||
| }), | ||
| server: createFixturePetstoreServer(), | ||
| }); | ||
| const result = await agent.invoke({ | ||
| messages: [ | ||
| { | ||
| role: "user", | ||
| content: "Which pets are currently available?", | ||
| }, | ||
| ], | ||
| }); | ||
| const toolMessage = result.messages.find(ToolMessage.isInstance); | ||
| if (!toolMessage) { | ||
| throw new Error("Expected the LangChain agent to execute a Petstore tool."); | ||
| } | ||
|
|
||
| console.log( | ||
| JSON.stringify( | ||
| { | ||
| tool: "search_available_pets", | ||
| toolCallId: toolMessage.tool_call_id, | ||
| result: JSON.parse(toolMessage.content as string) as unknown, | ||
| }, | ||
| null, | ||
| 2, | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| if (import.meta.url === `file://${process.argv[1]}`) { | ||
| await run(); | ||
| } | ||
This file contains hidden or 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,30 @@ | ||
| import { createTypeMcpAgent } from "@theorvane/type-chain/typemcp"; | ||
|
|
||
| import { PetstoreServer } from "./typemcp-petstore-server.js"; | ||
|
|
||
| type PetstoreAgentModel = Parameters< | ||
| typeof createTypeMcpAgent<PetstoreServer> | ||
| >[0]["model"]; | ||
|
|
||
| export type CreatePetstoreAgentOptions = Readonly<{ | ||
| /** The application selects, configures, and authenticates this LangChain model. */ | ||
| model: PetstoreAgentModel; | ||
| /** Optional explicit Petstore resolver target; defaults to the live read-only server. */ | ||
| server?: PetstoreServer; | ||
|
sjungwon03-ai marked this conversation as resolved.
|
||
| }>; | ||
|
|
||
| /** | ||
| * Builds a real LangChain agent from the in-process TypeMCP Petstore tools. | ||
| * It does not choose a model provider, read credentials, or start an MCP transport. | ||
| */ | ||
| export async function createPetstoreAgent({ | ||
| model, | ||
| server, | ||
| }: CreatePetstoreAgentOptions) { | ||
| const resolvedServer = server ?? new PetstoreServer(); | ||
| return createTypeMcpAgent({ | ||
| model, | ||
| server: PetstoreServer, | ||
| resolver: { resolve: () => resolvedServer }, | ||
| }); | ||
| } | ||
This file contains hidden or 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 hidden or 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,16 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { runExample } from "./run-example.js"; | ||
|
|
||
| describe("real TypeChain Swagger Petstore agent", () => { | ||
| it("runs a LangChain agent loop that selects and executes the TypeMCP search tool", () => { | ||
| expect(runExample("example:petstore:agent:real:fixture")).toEqual({ | ||
| tool: "search_available_pets", | ||
| toolCallId: "available-pets-call", | ||
| result: [ | ||
| { id: 1, name: "Milo", status: "available" }, | ||
| { id: 2, name: "Nori", status: "available" }, | ||
| ], | ||
| }); | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.