Repository navigation
Expand file tree
/
Copy pathGrokTextGeneration.test.ts
More file actions
136 lines (124 loc) · 5.24 KB
/
Copy pathGrokTextGeneration.test.ts
File metadata and controls
136 lines (124 loc) · 5.24 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
126
127
128
129
130
131
132
133
134
135
136
// @effect-diagnostics nodeBuiltinImport:off
import * as NodePath from "node:path";
import * as NodeOS from "node:os";
import * as NodeURL from "node:url";
import * as NodeFS from "node:fs";
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import { createModelSelection } from "@t3tools/shared/model";
import { expect } from "vite-plus/test";
import { ProviderInstanceId } from "@t3tools/contracts";
import { GrokSettings } from "@t3tools/provider-grok/settings";
import * as ServerConfig from "../config.ts";
import * as TextGeneration from "./TextGeneration.ts";
import { makeGrokTextGeneration } from "@t3tools/provider-grok/testing";
import { execScriptSource, writeFakeCli } from "@t3tools/provider-testing/fakeCli";
const decodeGrokSettings = Schema.decodeSync(GrokSettings);
const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
const mockAgentPath = NodePath.join(__dirname, "../provider/testFixtures/grok-text-mock-agent.mjs");
const layerGrokTextGenerationTest = ServerConfig.ServerConfig.layerTest(process.cwd(), {
prefix: "t3code-grok-text-generation-test-",
}).pipe(Layer.provideMerge(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer)));
function makeAcpGrokWrapper(dir: string, env: Record<string, string>): string {
return writeFakeCli({
directory: NodePath.join(dir, "bin"),
name: "grok",
env,
source: execScriptSource({
scriptPath: mockAgentPath,
expectedArgs: ["agent", "stdio"],
}),
});
}
function withFakeAcpGrok<A, E, R>(
env: Record<string, string>,
effectFn: (textGeneration: TextGeneration.TextGeneration["Service"]) => Effect.Effect<A, E, R>,
) {
return Effect.gen(function* () {
const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-acp-"));
yield* Effect.addFinalizer(() =>
Effect.sync(() => {
NodeFS.rmSync(tempDir, { recursive: true, force: true });
}),
);
const binaryPath = makeAcpGrokWrapper(tempDir, env);
const config = decodeGrokSettings({ binaryPath });
const textGeneration = yield* makeGrokTextGeneration(config);
return yield* effectFn(textGeneration);
}).pipe(Effect.scoped);
}
function readJsonRpcRequests(
filePath: string,
): ReadonlyArray<{ readonly method?: string; readonly params?: Record<string, unknown> }> {
return NodeFS.readFileSync(filePath, "utf8")
.trim()
.split("\n")
.filter((line) => line.length > 0)
.map((line) => JSON.parse(line) as { method?: string; params?: Record<string, unknown> });
}
it.layer(layerGrokTextGenerationTest)("GrokTextGeneration", (it) => {
it.effect("uses ACP with disabled tool capabilities and forwards the requested model id", () => {
const requestLogDir = NodeFS.mkdtempSync(
NodePath.join(NodeOS.tmpdir(), "t3code-grok-text-log-"),
);
const requestLogPath = NodePath.join(requestLogDir, "requests.ndjson");
return withFakeAcpGrok(
{
T3_ACP_REQUEST_LOG_PATH: requestLogPath,
T3_ACP_PROMPT_RESPONSE_TEXT: JSON.stringify({
subject: "Add Grok provider",
body: "Wire up the ACP runtime and headless text generation path.",
}),
},
(textGeneration) =>
Effect.gen(function* () {
const generated = yield* textGeneration.generateCommitMessage({
cwd: process.cwd(),
branch: "feature/grok",
stagedSummary: "M apps/server/src/provider/Drivers/GrokDriver.ts",
stagedPatch: "diff --git a/.../GrokDriver.ts b/.../GrokDriver.ts",
modelSelection: createModelSelection(ProviderInstanceId.make("grok"), "grok-mock-alt"),
});
expect(generated.subject).toBe("Add Grok provider");
expect(generated.body).toBe("Wire up the ACP runtime and headless text generation path.");
const requests = readJsonRpcRequests(requestLogPath);
expect(
requests.find((request) => request.method === "initialize")?.params?.clientCapabilities,
).toMatchObject({
fs: { readTextFile: false, writeTextFile: false },
terminal: false,
});
expect(
requests.some(
(request) =>
request.method === "session/set_model" &&
request.params?.modelId === "grok-mock-alt",
),
).toBe(true);
}),
);
});
it.effect("extracts the JSON object when Grok wraps it in conversational text", () =>
withFakeAcpGrok(
{
T3_ACP_PROMPT_RESPONSE_TEXT:
"Sure! Here's a thread title:\n\n" +
JSON.stringify({ title: "Investigate failing CI" }) +
"\n\nLet me know if you need anything else.",
},
(textGeneration) =>
Effect.gen(function* () {
const generated = yield* textGeneration.generateThreadTitle({
cwd: process.cwd(),
message: "the lint job is red",
modelSelection: createModelSelection(ProviderInstanceId.make("grok"), "grok-mock-alt"),
});
expect(generated.title).toBe("Investigate failing CI");
}),
),
);
});