-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathlangchain.ts
More file actions
125 lines (118 loc) · 3.55 KB
/
langchain.ts
File metadata and controls
125 lines (118 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
* LangChain.js integration for Bashkit.
*
* Provides LangChain-compatible tools wrapping BashTool and ScriptedTool
* for use with LangChain agents and chains.
*
* @example
* ```typescript
* import { createBashTool, createScriptedTool } from '@everruns/bashkit/langchain';
*
* // Basic bash tool
* const tool = createBashTool();
* const result = await tool.invoke({ commands: "echo hello" });
*
* // Scripted tool
* import { ScriptedTool } from '@everruns/bashkit';
* const st = new ScriptedTool({ name: "api" });
* st.addTool("greet", "Greet user", (p) => `hello ${p.name}\n`);
* const langchainTool = createScriptedTool(st);
* ```
*
* @packageDocumentation
*/
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
import { BashTool, ScriptedTool } from "./wrapper.js";
import type { BashOptions } from "./wrapper.js";
const bashInputSchema = z.object({
commands: z
.string()
.describe("Bash commands to execute (like `bash -c 'commands'`)"),
});
/**
* Format an execution result for LangChain tool output.
*/
function formatResult(result: {
stdout: string;
stderr: string;
exitCode: number;
error?: string | null;
}): string {
if (result.error) {
throw new Error(`Execution error: ${result.error}`);
}
let output = result.stdout;
if (result.stderr) {
output += `\nSTDERR: ${result.stderr}`;
}
if (result.exitCode !== 0) {
output += `\n[Exit code: ${result.exitCode}]`;
}
return output;
}
/**
* Create a LangChain-compatible Bashkit tool.
*
* Returns a `DynamicStructuredTool` that can be passed directly to
* LangChain agents like `createReactAgent`.
*
* @param options - BashTool configuration (username, hostname, limits)
*
* @example
* ```typescript
* import { createBashTool } from '@everruns/bashkit/langchain';
* import { createReactAgent } from '@langchain/langgraph/prebuilt';
*
* const tool = createBashTool({ username: "agent" });
* const agent = createReactAgent({ llm: model, tools: [tool] });
* ```
*/
export function createBashTool(
options?: Omit<BashOptions, "files">,
): DynamicStructuredTool {
const bashTool = new BashTool(options);
return new DynamicStructuredTool({
name: bashTool.name,
description: [
bashTool.shortDescription,
"Execute bash commands in a sandboxed virtual filesystem.",
"State persists between calls. Use for file operations, text processing, and scripting.",
].join(" "),
schema: bashInputSchema,
func: async ({ commands }: { commands: string }) => {
const result = bashTool.executeSync(commands);
return formatResult(result);
},
});
}
/**
* Create a LangChain-compatible tool from a configured ScriptedTool.
*
* The ScriptedTool should already have tools registered via `addTool()`.
*
* @param scriptedTool - A ScriptedTool with registered tool callbacks
*
* @example
* ```typescript
* import { ScriptedTool } from '@everruns/bashkit';
* import { createScriptedTool } from '@everruns/bashkit/langchain';
*
* const st = new ScriptedTool({ name: "api" });
* st.addTool("get_data", "Fetch data", (p) => JSON.stringify({ id: p.id }));
* const tool = createScriptedTool(st);
* ```
*/
export function createScriptedTool(
scriptedTool: ScriptedTool,
): DynamicStructuredTool {
return new DynamicStructuredTool({
name: scriptedTool.name,
description: scriptedTool.systemPrompt(),
schema: bashInputSchema,
func: async ({ commands }: { commands: string }) => {
const result = await scriptedTool.execute(commands);
return formatResult(result);
},
});
}