Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions apps/server/src/provider/testFixtures/grok-text-mock-agent.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ for await (const line of NodeReadline.createInterface({ input: process.stdin }))
NodeFS.appendFileSync(process.env.T3_ACP_REQUEST_LOG_PATH, `${line}\n`);
}
if (request.id === undefined) continue;
if (request.method === "session/prompt" && process.env.T3_ACP_PROMPT_ERROR_MESSAGE) {
write({
jsonrpc: "2.0",
id: request.id,
error: {
code: Number(process.env.T3_ACP_PROMPT_ERROR_CODE ?? -32003),
message: process.env.T3_ACP_PROMPT_ERROR_MESSAGE,
},
});
continue;
}
let result;
switch (request.method) {
case "initialize":
Expand Down
55 changes: 55 additions & 0 deletions apps/server/src/textGeneration/GrokTextGeneration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,59 @@ it.layer(layerGrokTextGenerationTest)("GrokTextGeneration", (it) => {
}),
),
);

it.effect("maps ACP failures to safe details and preserves their cause", () =>
Effect.gen(function* () {
for (const [code, detail] of [
[-32003, "Grok usage limit reached. Try again later."],
[-32000, "Grok authentication is required."],
[-32603, "Grok ACP request failed."],
[-32602, "Grok ACP request failed."],
] as const) {
yield* withFakeAcpGrok(
{
T3_ACP_PROMPT_ERROR_MESSAGE: "private provider payload " + "x".repeat(4096),
T3_ACP_PROMPT_ERROR_CODE: String(code),
},
(textGeneration) =>
Effect.gen(function* () {
const error = yield* textGeneration
.generateThreadTitle({
cwd: process.cwd(),
message: "the lint job is red",
modelSelection: createModelSelection(
ProviderInstanceId.make("grok"),
"grok-mock-alt",
),
})
.pipe(Effect.flip);
expect(error.detail).toBe(detail);
expect(error.cause).toMatchObject({
code,
errorMessage: "private provider payload " + "x".repeat(4096),
});
}),
);
}
}),
);

it.effect("explains rejected model settings without copying provider text", () =>
withFakeAcpGrok({}, (textGeneration) =>
Effect.gen(function* () {
const error = yield* textGeneration
.generateThreadTitle({
cwd: process.cwd(),
message: "the lint job is red",
modelSelection: createModelSelection(
ProviderInstanceId.make("grok"),
"grok-mock-missing",
),
})
.pipe(Effect.flip);
expect(error.cause).toMatchObject({ code: -32602, errorMessage: "Unknown Grok model" });
expect(error.detail).toBe("Grok rejected the text generation model settings.");
}),
),
);
});
26 changes: 23 additions & 3 deletions packages/provider-grok/src/server/textGeneration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as Option from "effect/Option";
import * as Ref from "effect/Ref";
import * as Schema from "effect/Schema";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import type * as EffectAcpErrors from "effect-acp/errors";
import * as EffectAcpErrors from "effect-acp/errors";

import { TextGenerationError } from "@t3tools/contracts";
import type { GrokSettings } from "../settings.ts";
Expand All @@ -19,9 +19,24 @@ import {
resolveGrokAcpBaseModelId,
} from "./acpSupport.ts";

import { xAiRateLimitedErrorCode } from "./xaiAcpExtension.ts";

const GROK_TIMEOUT_MS = 180_000;

const isTextGenerationError = Schema.is(TextGenerationError);
const isAcpRequestError = Schema.is(EffectAcpErrors.AcpRequestError);

function grokAcpFailureDetail(cause: EffectAcpErrors.AcpError, fallback: string) {
if (!isAcpRequestError(cause)) return fallback;
switch (cause.code) {
case xAiRateLimitedErrorCode:
return "Grok usage limit reached. Try again later.";
case -32000:
return "Grok authentication is required.";
default:
return fallback;
}
}

export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* (
grokSettings: GrokSettings,
Expand Down Expand Up @@ -75,7 +90,12 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi
mapError: (cause) =>
new TextGenerationError({
operation,
detail: "Failed to set Grok ACP base model for text generation.",
detail: grokAcpFailureDetail(
cause,
isAcpRequestError(cause) && cause.code === -32602
? "Grok rejected the text generation model settings."
: "Failed to set Grok ACP base model for text generation.",
),
cause,
}),
});
Expand All @@ -98,7 +118,7 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi
? cause
: new TextGenerationError({
operation,
detail: "Grok ACP request failed.",
detail: grokAcpFailureDetail(cause, "Grok ACP request failed."),
cause,
}),
),
Expand Down
Loading