@shahmarasy/prodo 0.1.4 → 0.1.5
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 +201 -97
- package/bin/prodo.cjs +6 -6
- package/dist/agents/agent-registry.d.ts +13 -0
- package/dist/agents/agent-registry.js +79 -0
- package/dist/agents/anthropic/index.d.ts +9 -0
- package/dist/agents/anthropic/index.js +55 -0
- package/dist/agents/base.d.ts +25 -0
- package/dist/agents/base.js +71 -0
- package/dist/agents/google/index.d.ts +9 -0
- package/dist/agents/google/index.js +53 -0
- package/dist/agents/mock/index.d.ts +11 -0
- package/dist/agents/mock/index.js +26 -0
- package/dist/agents/openai/index.d.ts +9 -0
- package/dist/agents/openai/index.js +57 -0
- package/dist/agents/system-prompts.d.ts +3 -0
- package/dist/agents/system-prompts.js +32 -0
- package/dist/cli/agent-command-installer.d.ts +4 -0
- package/dist/cli/agent-command-installer.js +148 -0
- package/dist/cli/agent-ids.d.ts +15 -0
- package/dist/cli/agent-ids.js +49 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +144 -0
- package/dist/cli/fix-tui.d.ts +4 -0
- package/dist/cli/fix-tui.js +79 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +465 -0
- package/dist/cli/init-tui.d.ts +23 -0
- package/dist/cli/init-tui.js +176 -0
- package/dist/cli/init.d.ts +11 -0
- package/dist/cli/init.js +334 -0
- package/dist/cli/normalize-interactive.d.ts +8 -0
- package/dist/cli/normalize-interactive.js +167 -0
- package/dist/cli/preset-loader.d.ts +4 -0
- package/dist/cli/preset-loader.js +210 -0
- package/dist/core/artifact-registry.d.ts +11 -0
- package/dist/core/artifact-registry.js +49 -0
- package/dist/core/artifacts.d.ts +10 -0
- package/dist/core/artifacts.js +892 -0
- package/dist/core/clean.d.ts +10 -0
- package/dist/core/clean.js +74 -0
- package/dist/core/consistency.d.ts +8 -0
- package/dist/core/consistency.js +328 -0
- package/dist/core/constants.d.ts +7 -0
- package/dist/core/constants.js +64 -0
- package/dist/core/errors.d.ts +3 -0
- package/dist/core/errors.js +10 -0
- package/dist/core/fix.d.ts +31 -0
- package/dist/core/fix.js +188 -0
- package/dist/core/hook-executor.d.ts +1 -0
- package/dist/core/hook-executor.js +175 -0
- package/dist/core/markdown.d.ts +16 -0
- package/dist/core/markdown.js +81 -0
- package/dist/core/normalize.d.ts +8 -0
- package/dist/core/normalize.js +125 -0
- package/dist/core/normalized-brief.d.ts +48 -0
- package/dist/core/normalized-brief.js +182 -0
- package/dist/core/output-index.d.ts +13 -0
- package/dist/core/output-index.js +55 -0
- package/dist/core/paths.d.ts +17 -0
- package/dist/core/paths.js +80 -0
- package/dist/core/project-config.d.ts +14 -0
- package/dist/core/project-config.js +69 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.js +115 -0
- package/dist/core/settings.d.ts +7 -0
- package/dist/core/settings.js +35 -0
- package/dist/core/template-engine.d.ts +3 -0
- package/dist/core/template-engine.js +43 -0
- package/dist/core/template-resolver.d.ts +15 -0
- package/dist/core/template-resolver.js +46 -0
- package/dist/core/templates.d.ts +33 -0
- package/dist/core/templates.js +440 -0
- package/dist/core/terminology.d.ts +21 -0
- package/dist/core/terminology.js +143 -0
- package/dist/core/tracing.d.ts +21 -0
- package/dist/core/tracing.js +74 -0
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.js +5 -0
- package/dist/core/utils.d.ts +7 -0
- package/dist/core/utils.js +66 -0
- package/dist/core/validate.d.ts +10 -0
- package/dist/core/validate.js +226 -0
- package/dist/core/validator.d.ts +5 -0
- package/dist/core/validator.js +76 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +30 -0
- package/dist/core/workflow-commands.d.ts +7 -0
- package/dist/core/workflow-commands.js +29 -0
- package/dist/i18n/en.json +45 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +63 -0
- package/dist/i18n/tr.json +45 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +20 -6
- package/dist/providers/mock-provider.d.ts +1 -1
- package/dist/providers/mock-provider.js +7 -6
- package/dist/providers/openai-provider.d.ts +1 -1
- package/dist/providers/openai-provider.js +1 -1
- package/dist/skills/engine.d.ts +10 -0
- package/dist/skills/engine.js +75 -0
- package/dist/skills/fix-skill.d.ts +2 -0
- package/dist/skills/fix-skill.js +38 -0
- package/dist/skills/generate-artifact-skill.d.ts +2 -0
- package/dist/skills/generate-artifact-skill.js +32 -0
- package/dist/skills/generate-pipeline-skill.d.ts +2 -0
- package/dist/skills/generate-pipeline-skill.js +45 -0
- package/dist/skills/normalize-skill.d.ts +2 -0
- package/dist/skills/normalize-skill.js +29 -0
- package/dist/skills/types.d.ts +28 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/validate-skill.d.ts +2 -0
- package/dist/skills/validate-skill.js +29 -0
- package/package.json +74 -45
- package/src/agents/agent-registry.ts +93 -0
- package/src/agents/anthropic/index.ts +86 -0
- package/src/agents/anthropic/manifest.json +7 -0
- package/src/agents/base.ts +77 -0
- package/src/agents/google/index.ts +79 -0
- package/src/agents/google/manifest.json +7 -0
- package/src/agents/mock/index.ts +32 -0
- package/src/agents/mock/manifest.json +7 -0
- package/src/agents/openai/index.ts +83 -0
- package/src/agents/openai/manifest.json +7 -0
- package/src/agents/system-prompts.ts +35 -0
- package/src/{agent-command-installer.ts → cli/agent-command-installer.ts} +164 -164
- package/src/{agents.ts → cli/agent-ids.ts} +58 -58
- package/src/{doctor.ts → cli/doctor.ts} +157 -137
- package/src/cli/fix-tui.ts +111 -0
- package/src/{cli.ts → cli/index.ts} +459 -410
- package/src/{init-tui.ts → cli/init-tui.ts} +208 -208
- package/src/{init.ts → cli/init.ts} +398 -398
- package/src/cli/normalize-interactive.ts +241 -0
- package/src/{preset-loader.ts → cli/preset-loader.ts} +237 -237
- package/src/{artifact-registry.ts → core/artifact-registry.ts} +69 -69
- package/src/{artifacts.ts → core/artifacts.ts} +1081 -1072
- package/src/core/clean.ts +88 -0
- package/src/{consistency.ts → core/consistency.ts} +374 -303
- package/src/{constants.ts → core/constants.ts} +72 -72
- package/src/{errors.ts → core/errors.ts} +7 -7
- package/src/core/fix.ts +253 -0
- package/src/{hook-executor.ts → core/hook-executor.ts} +196 -196
- package/src/{markdown.ts → core/markdown.ts} +93 -73
- package/src/{normalize.ts → core/normalize.ts} +145 -137
- package/src/{normalized-brief.ts → core/normalized-brief.ts} +227 -206
- package/src/{output-index.ts → core/output-index.ts} +59 -59
- package/src/{paths.ts → core/paths.ts} +75 -71
- package/src/{project-config.ts → core/project-config.ts} +78 -78
- package/src/{registry.ts → core/registry.ts} +119 -119
- package/src/{settings.ts → core/settings.ts} +35 -35
- package/src/core/template-engine.ts +45 -0
- package/src/{template-resolver.ts → core/template-resolver.ts} +54 -54
- package/src/{templates.ts → core/templates.ts} +452 -452
- package/src/core/terminology.ts +177 -0
- package/src/core/tracing.ts +110 -0
- package/src/{types.ts → core/types.ts} +46 -46
- package/src/{utils.ts → core/utils.ts} +64 -64
- package/src/{validate.ts → core/validate.ts} +252 -246
- package/src/{validator.ts → core/validator.ts} +92 -92
- package/src/{version.ts → core/version.ts} +24 -24
- package/src/{workflow-commands.ts → core/workflow-commands.ts} +32 -32
- package/src/i18n/en.json +45 -0
- package/src/i18n/index.ts +58 -0
- package/src/i18n/tr.json +45 -0
- package/src/providers/index.ts +29 -12
- package/src/providers/mock-provider.ts +200 -199
- package/src/providers/openai-provider.ts +88 -88
- package/src/skills/engine.ts +94 -0
- package/src/skills/fix-skill.ts +38 -0
- package/src/skills/generate-artifact-skill.ts +32 -0
- package/src/skills/generate-pipeline-skill.ts +49 -0
- package/src/skills/normalize-skill.ts +29 -0
- package/src/skills/types.ts +36 -0
- package/src/skills/validate-skill.ts +29 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { AgentPlugin } from "./base";
|
|
2
|
+
import { UserError } from "../core/errors";
|
|
3
|
+
import type { LLMProvider } from "../core/types";
|
|
4
|
+
import { MockAgent } from "./mock";
|
|
5
|
+
import { OpenAIAgent } from "./openai";
|
|
6
|
+
import { AnthropicAgent } from "./anthropic";
|
|
7
|
+
import { GoogleAgent } from "./google";
|
|
8
|
+
|
|
9
|
+
const AGENT_ALIASES: Record<string, string> = {
|
|
10
|
+
mock: "mock",
|
|
11
|
+
openai: "openai",
|
|
12
|
+
anthropic: "anthropic",
|
|
13
|
+
claude: "anthropic",
|
|
14
|
+
google: "google",
|
|
15
|
+
gemini: "google"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class AgentRegistry {
|
|
19
|
+
private agents = new Map<string, AgentPlugin>();
|
|
20
|
+
|
|
21
|
+
register(agent: AgentPlugin): void {
|
|
22
|
+
this.agents.set(agent.name, agent);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
list(): AgentPlugin[] {
|
|
26
|
+
return Array.from(this.agents.values());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get(name: string): AgentPlugin | undefined {
|
|
30
|
+
const normalized = AGENT_ALIASES[name.toLowerCase().trim()] ?? name.toLowerCase().trim();
|
|
31
|
+
return this.agents.get(normalized);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async resolve(name?: string): Promise<AgentPlugin> {
|
|
35
|
+
const agentName = this.resolveAgentName(name);
|
|
36
|
+
const agent = this.get(agentName);
|
|
37
|
+
|
|
38
|
+
if (!agent) {
|
|
39
|
+
const available = this.list().map((a) => a.name).join(", ");
|
|
40
|
+
throw new UserError(
|
|
41
|
+
`Unknown agent: "${agentName}". Available agents: ${available}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return agent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private resolveAgentName(override?: string): string {
|
|
49
|
+
if (override) return override;
|
|
50
|
+
|
|
51
|
+
const fromEnv = process.env.PRODO_AGENT;
|
|
52
|
+
if (fromEnv) return fromEnv;
|
|
53
|
+
|
|
54
|
+
const fromLegacy = process.env.PRODO_LLM_PROVIDER;
|
|
55
|
+
if (fromLegacy) return fromLegacy;
|
|
56
|
+
|
|
57
|
+
const isTest =
|
|
58
|
+
process.env.NODE_ENV === "test" ||
|
|
59
|
+
process.env.PRODO_TEST === "1";
|
|
60
|
+
if (isTest) return "mock";
|
|
61
|
+
|
|
62
|
+
return "mock";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
toProvider(agent: AgentPlugin): LLMProvider {
|
|
66
|
+
return {
|
|
67
|
+
generate: (prompt, inputContext, schemaHint) =>
|
|
68
|
+
agent.generate(prompt, inputContext, schemaHint)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let globalRegistry: AgentRegistry | null = null;
|
|
74
|
+
|
|
75
|
+
function createDefaultRegistry(): AgentRegistry {
|
|
76
|
+
const registry = new AgentRegistry();
|
|
77
|
+
registry.register(new MockAgent());
|
|
78
|
+
registry.register(new OpenAIAgent());
|
|
79
|
+
registry.register(new AnthropicAgent());
|
|
80
|
+
registry.register(new GoogleAgent());
|
|
81
|
+
return registry;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function getGlobalRegistry(): AgentRegistry {
|
|
85
|
+
if (!globalRegistry) {
|
|
86
|
+
globalRegistry = createDefaultRegistry();
|
|
87
|
+
}
|
|
88
|
+
return globalRegistry;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function resetGlobalRegistry(): void {
|
|
92
|
+
globalRegistry = null;
|
|
93
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { BaseAgent, type AgentConfig } from "../base";
|
|
2
|
+
import { buildSystemPrompt, buildUserMessage } from "../system-prompts";
|
|
3
|
+
import { UserError } from "../../core/errors";
|
|
4
|
+
import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
|
|
5
|
+
|
|
6
|
+
const dynamicImport = new Function("specifier", "return import(specifier)") as (
|
|
7
|
+
specifier: string
|
|
8
|
+
) => Promise<unknown>;
|
|
9
|
+
|
|
10
|
+
type ContentBlock = { type: string; text?: string };
|
|
11
|
+
|
|
12
|
+
export class AnthropicAgent extends BaseAgent {
|
|
13
|
+
readonly name = "anthropic";
|
|
14
|
+
readonly displayName = "Anthropic Claude";
|
|
15
|
+
readonly sdkRequired = "@anthropic-ai/sdk";
|
|
16
|
+
|
|
17
|
+
getConfig(): AgentConfig {
|
|
18
|
+
return {
|
|
19
|
+
name: this.name,
|
|
20
|
+
displayName: this.displayName,
|
|
21
|
+
sdkRequired: this.sdkRequired,
|
|
22
|
+
envVars: ["ANTHROPIC_API_KEY"],
|
|
23
|
+
defaultModel: "claude-sonnet-4-20250514"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async generate(
|
|
28
|
+
prompt: string,
|
|
29
|
+
inputContext: Record<string, unknown>,
|
|
30
|
+
schemaHint: ProviderSchemaHint
|
|
31
|
+
): Promise<GenerateResult> {
|
|
32
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
33
|
+
if (!apiKey) {
|
|
34
|
+
throw new UserError("ANTHROPIC_API_KEY is not set. Set it to use the Anthropic agent.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const model = process.env.PRODO_ANTHROPIC_MODEL ?? "claude-sonnet-4-20250514";
|
|
38
|
+
|
|
39
|
+
const outputLanguage =
|
|
40
|
+
typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
41
|
+
? inputContext.outputLanguage.trim()
|
|
42
|
+
: "en";
|
|
43
|
+
|
|
44
|
+
const system = buildSystemPrompt(schemaHint, outputLanguage);
|
|
45
|
+
const user = buildUserMessage(prompt, inputContext);
|
|
46
|
+
|
|
47
|
+
let AnthropicConstructor: new (opts: { apiKey: string }) => {
|
|
48
|
+
messages: {
|
|
49
|
+
create: (params: Record<string, unknown>) => Promise<{
|
|
50
|
+
content: ContentBlock[];
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const mod = (await dynamicImport("@anthropic-ai/sdk")) as {
|
|
57
|
+
default: typeof AnthropicConstructor;
|
|
58
|
+
};
|
|
59
|
+
AnthropicConstructor = mod.default;
|
|
60
|
+
} catch {
|
|
61
|
+
throw new UserError(
|
|
62
|
+
"Anthropic SDK is not installed. Run: npm install @anthropic-ai/sdk"
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const client = new AnthropicConstructor({ apiKey });
|
|
67
|
+
|
|
68
|
+
const response = await client.messages.create({
|
|
69
|
+
model,
|
|
70
|
+
max_tokens: 8192,
|
|
71
|
+
system,
|
|
72
|
+
messages: [{ role: "user", content: user }]
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const textBlock = response.content.find(
|
|
76
|
+
(block: ContentBlock) => block.type === "text"
|
|
77
|
+
);
|
|
78
|
+
const content = textBlock?.text?.trim() ?? "";
|
|
79
|
+
|
|
80
|
+
if (!content) {
|
|
81
|
+
throw new UserError("Anthropic agent returned an empty response.");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { body: content };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { GenerateResult, ProviderSchemaHint } from "../core/types";
|
|
2
|
+
|
|
3
|
+
export type AgentConfig = {
|
|
4
|
+
name: string;
|
|
5
|
+
displayName: string;
|
|
6
|
+
sdkRequired: string | null;
|
|
7
|
+
envVars: string[];
|
|
8
|
+
defaultModel?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface AgentPlugin {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly displayName: string;
|
|
14
|
+
readonly sdkRequired: string | null;
|
|
15
|
+
|
|
16
|
+
generate(
|
|
17
|
+
prompt: string,
|
|
18
|
+
inputContext: Record<string, unknown>,
|
|
19
|
+
schemaHint: ProviderSchemaHint
|
|
20
|
+
): Promise<GenerateResult>;
|
|
21
|
+
|
|
22
|
+
isAvailable(): Promise<boolean>;
|
|
23
|
+
getConfig(): AgentConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export abstract class BaseAgent implements AgentPlugin {
|
|
27
|
+
abstract readonly name: string;
|
|
28
|
+
abstract readonly displayName: string;
|
|
29
|
+
abstract readonly sdkRequired: string | null;
|
|
30
|
+
|
|
31
|
+
abstract generate(
|
|
32
|
+
prompt: string,
|
|
33
|
+
inputContext: Record<string, unknown>,
|
|
34
|
+
schemaHint: ProviderSchemaHint
|
|
35
|
+
): Promise<GenerateResult>;
|
|
36
|
+
|
|
37
|
+
abstract getConfig(): AgentConfig;
|
|
38
|
+
|
|
39
|
+
async isAvailable(): Promise<boolean> {
|
|
40
|
+
const config = this.getConfig();
|
|
41
|
+
|
|
42
|
+
if (config.sdkRequired) {
|
|
43
|
+
try {
|
|
44
|
+
await import(config.sdkRequired);
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const envVar of config.envVars) {
|
|
51
|
+
if (!process.env[envVar]) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
availabilityHint(): string {
|
|
60
|
+
const config = this.getConfig();
|
|
61
|
+
const hints: string[] = [];
|
|
62
|
+
|
|
63
|
+
if (config.sdkRequired) {
|
|
64
|
+
hints.push(`SDK required: npm install ${config.sdkRequired}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const envVar of config.envVars) {
|
|
68
|
+
if (!process.env[envVar]) {
|
|
69
|
+
hints.push(`Set environment variable: ${envVar}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return hints.length > 0
|
|
74
|
+
? `Agent "${config.displayName}" is not available.\n ${hints.join("\n ")}`
|
|
75
|
+
: `Agent "${config.displayName}" is available.`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { BaseAgent, type AgentConfig } from "../base";
|
|
2
|
+
import { buildSystemPrompt, buildUserMessage } from "../system-prompts";
|
|
3
|
+
import { UserError } from "../../core/errors";
|
|
4
|
+
import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
|
|
5
|
+
|
|
6
|
+
const dynamicImport = new Function("specifier", "return import(specifier)") as (
|
|
7
|
+
specifier: string
|
|
8
|
+
) => Promise<unknown>;
|
|
9
|
+
|
|
10
|
+
export class GoogleAgent extends BaseAgent {
|
|
11
|
+
readonly name = "google";
|
|
12
|
+
readonly displayName = "Google Gemini";
|
|
13
|
+
readonly sdkRequired = "@google/generative-ai";
|
|
14
|
+
|
|
15
|
+
getConfig(): AgentConfig {
|
|
16
|
+
return {
|
|
17
|
+
name: this.name,
|
|
18
|
+
displayName: this.displayName,
|
|
19
|
+
sdkRequired: this.sdkRequired,
|
|
20
|
+
envVars: ["GOOGLE_API_KEY"],
|
|
21
|
+
defaultModel: "gemini-2.0-flash"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async generate(
|
|
26
|
+
prompt: string,
|
|
27
|
+
inputContext: Record<string, unknown>,
|
|
28
|
+
schemaHint: ProviderSchemaHint
|
|
29
|
+
): Promise<GenerateResult> {
|
|
30
|
+
const apiKey = process.env.GOOGLE_API_KEY;
|
|
31
|
+
if (!apiKey) {
|
|
32
|
+
throw new UserError("GOOGLE_API_KEY is not set. Set it to use the Google Gemini agent.");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const model = process.env.PRODO_GOOGLE_MODEL ?? "gemini-2.0-flash";
|
|
36
|
+
|
|
37
|
+
const outputLanguage =
|
|
38
|
+
typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
39
|
+
? inputContext.outputLanguage.trim()
|
|
40
|
+
: "en";
|
|
41
|
+
|
|
42
|
+
const system = buildSystemPrompt(schemaHint, outputLanguage);
|
|
43
|
+
const user = buildUserMessage(prompt, inputContext);
|
|
44
|
+
|
|
45
|
+
let GoogleGenerativeAI: new (apiKey: string) => {
|
|
46
|
+
getGenerativeModel: (config: { model: string; systemInstruction: string }) => {
|
|
47
|
+
generateContent: (content: string) => Promise<{
|
|
48
|
+
response: { text: () => string };
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const mod = (await dynamicImport("@google/generative-ai")) as {
|
|
55
|
+
GoogleGenerativeAI: typeof GoogleGenerativeAI;
|
|
56
|
+
};
|
|
57
|
+
GoogleGenerativeAI = mod.GoogleGenerativeAI;
|
|
58
|
+
} catch {
|
|
59
|
+
throw new UserError(
|
|
60
|
+
"Google Generative AI SDK is not installed. Run: npm install @google/generative-ai"
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const client = new GoogleGenerativeAI(apiKey);
|
|
65
|
+
const generativeModel = client.getGenerativeModel({
|
|
66
|
+
model,
|
|
67
|
+
systemInstruction: system
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await generativeModel.generateContent(user);
|
|
71
|
+
const content = result.response.text().trim();
|
|
72
|
+
|
|
73
|
+
if (!content) {
|
|
74
|
+
throw new UserError("Google Gemini agent returned an empty response.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { body: content };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BaseAgent, type AgentConfig } from "../base";
|
|
2
|
+
import { MockProvider } from "../../providers/mock-provider";
|
|
3
|
+
import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
|
|
4
|
+
|
|
5
|
+
export class MockAgent extends BaseAgent {
|
|
6
|
+
readonly name = "mock";
|
|
7
|
+
readonly displayName = "Mock (Testing)";
|
|
8
|
+
readonly sdkRequired = null;
|
|
9
|
+
|
|
10
|
+
private readonly provider = new MockProvider();
|
|
11
|
+
|
|
12
|
+
async generate(
|
|
13
|
+
prompt: string,
|
|
14
|
+
inputContext: Record<string, unknown>,
|
|
15
|
+
schemaHint: ProviderSchemaHint
|
|
16
|
+
): Promise<GenerateResult> {
|
|
17
|
+
return this.provider.generate(prompt, inputContext, schemaHint);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async isAvailable(): Promise<boolean> {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getConfig(): AgentConfig {
|
|
25
|
+
return {
|
|
26
|
+
name: this.name,
|
|
27
|
+
displayName: this.displayName,
|
|
28
|
+
sdkRequired: null,
|
|
29
|
+
envVars: []
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { BaseAgent, type AgentConfig } from "../base";
|
|
2
|
+
import { buildSystemPrompt, buildUserMessage } from "../system-prompts";
|
|
3
|
+
import { UserError } from "../../core/errors";
|
|
4
|
+
import type { GenerateResult, ProviderSchemaHint } from "../../core/types";
|
|
5
|
+
|
|
6
|
+
const dynamicImport = new Function("specifier", "return import(specifier)") as (
|
|
7
|
+
specifier: string
|
|
8
|
+
) => Promise<unknown>;
|
|
9
|
+
|
|
10
|
+
export class OpenAIAgent extends BaseAgent {
|
|
11
|
+
readonly name = "openai";
|
|
12
|
+
readonly displayName = "OpenAI";
|
|
13
|
+
readonly sdkRequired = "openai";
|
|
14
|
+
|
|
15
|
+
getConfig(): AgentConfig {
|
|
16
|
+
return {
|
|
17
|
+
name: this.name,
|
|
18
|
+
displayName: this.displayName,
|
|
19
|
+
sdkRequired: this.sdkRequired,
|
|
20
|
+
envVars: ["OPENAI_API_KEY"],
|
|
21
|
+
defaultModel: "gpt-4o-mini"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async generate(
|
|
26
|
+
prompt: string,
|
|
27
|
+
inputContext: Record<string, unknown>,
|
|
28
|
+
schemaHint: ProviderSchemaHint
|
|
29
|
+
): Promise<GenerateResult> {
|
|
30
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
31
|
+
if (!apiKey) {
|
|
32
|
+
throw new UserError("OPENAI_API_KEY is not set. Set it to use the OpenAI agent.");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const model = process.env.PRODO_OPENAI_MODEL ?? "gpt-4o-mini";
|
|
36
|
+
const baseURL = process.env.PRODO_OPENAI_BASE_URL ?? undefined;
|
|
37
|
+
|
|
38
|
+
const outputLanguage =
|
|
39
|
+
typeof inputContext.outputLanguage === "string" && inputContext.outputLanguage.trim()
|
|
40
|
+
? inputContext.outputLanguage.trim()
|
|
41
|
+
: "en";
|
|
42
|
+
|
|
43
|
+
const system = buildSystemPrompt(schemaHint, outputLanguage);
|
|
44
|
+
const user = buildUserMessage(prompt, inputContext);
|
|
45
|
+
|
|
46
|
+
let OpenAIConstructor: new (opts: { apiKey: string; baseURL?: string }) => {
|
|
47
|
+
chat: {
|
|
48
|
+
completions: {
|
|
49
|
+
create: (params: Record<string, unknown>) => Promise<{
|
|
50
|
+
choices: Array<{ message?: { content?: string } }>;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const mod = (await dynamicImport("openai")) as { default: typeof OpenAIConstructor };
|
|
58
|
+
OpenAIConstructor = mod.default;
|
|
59
|
+
} catch {
|
|
60
|
+
throw new UserError(
|
|
61
|
+
"OpenAI SDK is not installed. Run: npm install openai"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const client = new OpenAIConstructor({ apiKey, baseURL });
|
|
66
|
+
|
|
67
|
+
const response = await client.chat.completions.create({
|
|
68
|
+
model,
|
|
69
|
+
messages: [
|
|
70
|
+
{ role: "system", content: system },
|
|
71
|
+
{ role: "user", content: user }
|
|
72
|
+
],
|
|
73
|
+
temperature: 0.2
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const content = response.choices[0]?.message?.content?.trim();
|
|
77
|
+
if (!content) {
|
|
78
|
+
throw new UserError("OpenAI agent returned an empty response.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { body: content };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ProviderSchemaHint } from "../core/types";
|
|
2
|
+
|
|
3
|
+
export function buildSystemPrompt(schemaHint: ProviderSchemaHint, outputLanguage: string): string {
|
|
4
|
+
const mode = schemaHint.artifactType;
|
|
5
|
+
|
|
6
|
+
if (mode === "normalize") {
|
|
7
|
+
return `You normalize messy human product briefs into strict JSON.
|
|
8
|
+
Return valid JSON only, no markdown. Include confidence scores (0..1) for critical fields.
|
|
9
|
+
Preserve source language and Unicode characters exactly; never transliterate Turkish letters to ASCII.`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (mode === "semantic_consistency") {
|
|
13
|
+
return `You detect semantic inconsistencies between paired artifacts.
|
|
14
|
+
Return valid JSON only: { "issues": [{level, code, check, contract_id, file, message, suggestion}] }.`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (mode === "contract_relevance") {
|
|
18
|
+
return `You verify whether tagged content actually matches the referenced contract text.
|
|
19
|
+
Return valid JSON only: { "relevant": boolean, "score": number, "reason": string }.`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return `You are a product-document generator.
|
|
23
|
+
Return only Markdown body content.
|
|
24
|
+
Headings required:
|
|
25
|
+
${schemaHint.requiredHeadings.join("\n")}
|
|
26
|
+
Required contract tags:
|
|
27
|
+
${schemaHint.requiredContracts.join(", ")}
|
|
28
|
+
Use tags like [G1], [F2], [C1] where relevant.
|
|
29
|
+
Output language: ${outputLanguage}
|
|
30
|
+
Do not translate required headings.`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function buildUserMessage(prompt: string, inputContext: Record<string, unknown>): string {
|
|
34
|
+
return `${prompt}\n\nContext JSON:\n${JSON.stringify(inputContext, null, 2)}`;
|
|
35
|
+
}
|