@alint-js/agent-pi 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # `@alint-js/agent-pi`
2
+
3
+ > [!IMPORTANT]
4
+ > This package is a WIP. APIs may be subject to major changes.
5
+
6
+ A [Pi](https://github.com/earendil-works/pi)-backed `AgentAdapter` for alint. One of the swappable vendor adapters behind `@alint-js/agent`.
7
+
8
+ ## What it does
9
+
10
+ `createPiAdapter()` returns an `AgentAdapter` that runs a rule's request through a Pi
11
+ `Agent`: it translates the framework-agnostic `AgentTool`s to Pi's TypeBox tools, runs
12
+ the tool loop, and reads back the final assistant message.
13
+
14
+ ## How to use
15
+
16
+ ```ts
17
+ import { createPiAdapter } from '@alint-js/agent-pi'
18
+
19
+ const adapter = createPiAdapter()
20
+ ```
21
+
22
+ Pass `{ run }` to inject a custom Pi run.
23
+
24
+ ## When to use
25
+
26
+ - You want tool-using rules powered by Pi (`pi-ai` and `pi-agent-core`).
27
+
28
+ ## When not to use
29
+
30
+ - You use a different agent framework. Pick that vendor's adapter (e.g.
31
+ `@alint-js/agent-apeira`) or write your own `AgentAdapter`.
32
+ - Your rule needs no tools. Keep it on the plain `@alint-js/core` rule DSL.
@@ -0,0 +1,20 @@
1
+ import { AgentTool } from "@earendil-works/pi-agent-core";
2
+ import { Model } from "@earendil-works/pi-ai";
3
+ import { AgentAdapter, AgentRequest, AgentTool as AgentTool$1 } from "@alint-js/agent";
4
+ import { ResolvedModel } from "@alint-js/core";
5
+
6
+ //#region src/index.d.ts
7
+ interface PiAdapterOptions {
8
+ run: (request: AgentRequest) => Promise<PiMessage[]>;
9
+ }
10
+ interface PiMessage {
11
+ content?: unknown;
12
+ role?: unknown;
13
+ }
14
+ declare function apiKeyFromModel(model: ResolvedModel): string;
15
+ declare function createPiAdapter(options?: Partial<PiAdapterOptions>): AgentAdapter;
16
+ declare function createPiModel(model: ResolvedModel): Model<'openai-completions'>;
17
+ declare function extractPiText(message?: PiMessage): string;
18
+ declare function toPiTools(tools: AgentTool$1[]): AgentTool[];
19
+ //#endregion
20
+ export { PiAdapterOptions, apiKeyFromModel, createPiAdapter, createPiModel, extractPiText, toPiTools };
package/dist/index.mjs ADDED
@@ -0,0 +1,74 @@
1
+ import { Agent } from "@earendil-works/pi-agent-core";
2
+ import { Type } from "@earendil-works/pi-ai";
3
+ //#region src/index.ts
4
+ function apiKeyFromModel(model) {
5
+ return (model.provider.headers.Authorization ?? model.provider.headers.authorization)?.replace(/^Bearer\s+/i, "") ?? "unused";
6
+ }
7
+ function createPiAdapter(options = {}) {
8
+ const run = options.run ?? runPiAgent;
9
+ return async (request) => {
10
+ return {
11
+ answer: extractPiText([...await run(request)].reverse().find((message) => message.role === "assistant")),
12
+ usage: void 0
13
+ };
14
+ };
15
+ }
16
+ function createPiModel(model) {
17
+ return {
18
+ api: "openai-completions",
19
+ baseUrl: model.provider.endpoint,
20
+ contextWindow: model.contextWindow ?? 32768,
21
+ cost: {
22
+ cacheRead: 0,
23
+ cacheWrite: 0,
24
+ input: 0,
25
+ output: 0
26
+ },
27
+ id: model.id,
28
+ input: ["text"],
29
+ maxTokens: 4096,
30
+ name: model.name,
31
+ provider: model.provider.id,
32
+ reasoning: false
33
+ };
34
+ }
35
+ function extractPiText(message) {
36
+ if (!message) return "";
37
+ const { content } = message;
38
+ if (typeof content === "string") return content;
39
+ if (Array.isArray(content)) return content.filter((part) => isTextPart(part)).map((part) => part.text).join("");
40
+ return "";
41
+ }
42
+ function toPiTools(tools) {
43
+ return tools.map((agentTool) => ({
44
+ description: agentTool.description,
45
+ execute: async (_toolCallId, params) => ({
46
+ content: [{
47
+ text: String(await agentTool.execute(params)),
48
+ type: "text"
49
+ }],
50
+ details: void 0
51
+ }),
52
+ label: agentTool.name,
53
+ name: agentTool.name,
54
+ parameters: Type.Unsafe(agentTool.parameters)
55
+ }));
56
+ }
57
+ function isTextPart(part) {
58
+ return typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string";
59
+ }
60
+ async function runPiAgent(request) {
61
+ const agent = new Agent({
62
+ getApiKey: () => apiKeyFromModel(request.model),
63
+ initialState: {
64
+ model: createPiModel(request.model),
65
+ systemPrompt: request.instructions,
66
+ tools: toPiTools(request.tools)
67
+ }
68
+ });
69
+ await agent.prompt(request.prompt);
70
+ await agent.waitForIdle();
71
+ return agent.state.messages;
72
+ }
73
+ //#endregion
74
+ export { apiKeyFromModel, createPiAdapter, createPiModel, extractPiText, toPiTools };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@alint-js/agent-pi",
3
+ "type": "module",
4
+ "version": "0.0.4",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.mts",
8
+ "default": "./dist/index.mjs"
9
+ },
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "dependencies": {
16
+ "@earendil-works/pi-agent-core": "^0.80.2",
17
+ "@earendil-works/pi-ai": "^0.80.2",
18
+ "@smithy/node-http-handler": "^4.9.1",
19
+ "@alint-js/core": "0.0.4",
20
+ "@alint-js/agent": "0.0.4"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^26.0.1",
24
+ "tsdown": "^0.22.3",
25
+ "typescript": "^6.0.3",
26
+ "vitest": "^4.1.9"
27
+ },
28
+ "scripts": {
29
+ "build": "tsdown",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test": "vitest run --config vitest.config.ts"
32
+ }
33
+ }