@theone1345/smartrelay 0.2.0

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.
Files changed (64) hide show
  1. package/README.md +446 -0
  2. package/config/runners/agents.yaml +47 -0
  3. package/config/runners/anthropic.yaml +23 -0
  4. package/config/runners/nvidia.yaml +53 -0
  5. package/config/runners/ollama.yaml +33 -0
  6. package/config/runners/openai.yaml +23 -0
  7. package/config/runners/openrouter.yaml +83 -0
  8. package/config.yaml +16 -0
  9. package/dist/benchmark/engine.d.ts +49 -0
  10. package/dist/benchmark/engine.js +147 -0
  11. package/dist/benchmark/engine.js.map +1 -0
  12. package/dist/benchmark/scorers.d.ts +59 -0
  13. package/dist/benchmark/scorers.js +241 -0
  14. package/dist/benchmark/scorers.js.map +1 -0
  15. package/dist/http-api.d.ts +23 -0
  16. package/dist/http-api.js +329 -0
  17. package/dist/http-api.js.map +1 -0
  18. package/dist/index.d.ts +16 -0
  19. package/dist/index.js +17 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/logger.d.ts +22 -0
  22. package/dist/logger.js +34 -0
  23. package/dist/logger.js.map +1 -0
  24. package/dist/router.d.ts +33 -0
  25. package/dist/router.js +298 -0
  26. package/dist/router.js.map +1 -0
  27. package/dist/runners/anthropic.d.ts +15 -0
  28. package/dist/runners/anthropic.js +116 -0
  29. package/dist/runners/anthropic.js.map +1 -0
  30. package/dist/runners/base.d.ts +90 -0
  31. package/dist/runners/base.js +61 -0
  32. package/dist/runners/base.js.map +1 -0
  33. package/dist/runners/nvidia.d.ts +14 -0
  34. package/dist/runners/nvidia.js +23 -0
  35. package/dist/runners/nvidia.js.map +1 -0
  36. package/dist/runners/ollama.d.ts +8 -0
  37. package/dist/runners/ollama.js +107 -0
  38. package/dist/runners/ollama.js.map +1 -0
  39. package/dist/runners/openai.d.ts +28 -0
  40. package/dist/runners/openai.js +131 -0
  41. package/dist/runners/openai.js.map +1 -0
  42. package/dist/runners/openrouter.d.ts +11 -0
  43. package/dist/runners/openrouter.js +19 -0
  44. package/dist/runners/openrouter.js.map +1 -0
  45. package/dist/runners/registry.d.ts +53 -0
  46. package/dist/runners/registry.js +273 -0
  47. package/dist/runners/registry.js.map +1 -0
  48. package/dist/server.d.ts +16 -0
  49. package/dist/server.js +305 -0
  50. package/dist/server.js.map +1 -0
  51. package/dist/tools/handlers.d.ts +49 -0
  52. package/dist/tools/handlers.js +331 -0
  53. package/dist/tools/handlers.js.map +1 -0
  54. package/dist/tools/index.d.ts +1 -0
  55. package/dist/tools/index.js +2 -0
  56. package/dist/tools/index.js.map +1 -0
  57. package/dist/util.d.ts +47 -0
  58. package/dist/util.js +140 -0
  59. package/dist/util.js.map +1 -0
  60. package/package.json +70 -0
  61. package/prompts/code_review.md +99 -0
  62. package/prompts/explain_code.md +79 -0
  63. package/prompts/planner.md +23 -0
  64. package/prompts/test_generator.md +2 -0
@@ -0,0 +1,83 @@
1
+ runners:
2
+ # ==========================================
3
+ # OpenRouter Cloud Models
4
+ # ==========================================
5
+ openrouter-claude-3.7-sonnet:
6
+ type: "openrouter"
7
+ model: "anthropic/claude-3.7-sonnet"
8
+ api_key_env: "OPENROUTER_API_KEY"
9
+ cost_per_million_input_tokens: 3.00
10
+ cost_per_million_output_tokens: 15.00
11
+ default_params:
12
+ max_tokens: 4096
13
+ temperature: 0.5
14
+
15
+ openrouter-deepseek-r1:
16
+ type: "openrouter"
17
+ model: "deepseek/deepseek-r1"
18
+ api_key_env: "OPENROUTER_API_KEY"
19
+ cost_per_million_input_tokens: 0.55
20
+ cost_per_million_output_tokens: 2.19
21
+ default_params:
22
+ max_tokens: 4096
23
+ temperature: 0.6
24
+
25
+ openrouter-deepseek-v3:
26
+ type: "openrouter"
27
+ model: "deepseek/deepseek-chat"
28
+ api_key_env: "OPENROUTER_API_KEY"
29
+ cost_per_million_input_tokens: 0.14
30
+ cost_per_million_output_tokens: 0.28
31
+ default_params:
32
+ max_tokens: 2048
33
+ temperature: 0.7
34
+
35
+ openrouter-qwen-2.5-coder:
36
+ type: "openrouter"
37
+ model: "qwen/qwen-2.5-coder-32b-instruct"
38
+ api_key_env: "OPENROUTER_API_KEY"
39
+ cost_per_million_input_tokens: 0.12
40
+ cost_per_million_output_tokens: 0.20
41
+ default_params:
42
+ max_tokens: 4096
43
+ temperature: 0.2
44
+
45
+ openrouter-llama-3.3-70b:
46
+ type: "openrouter"
47
+ model: "meta-llama/llama-3.3-70b-instruct"
48
+ api_key_env: "OPENROUTER_API_KEY"
49
+ cost_per_million_input_tokens: 0.12
50
+ cost_per_million_output_tokens: 0.30
51
+ default_params:
52
+ max_tokens: 4096
53
+ temperature: 0.7
54
+
55
+ openrouter-gemini-2.0-flash:
56
+ type: "openrouter"
57
+ model: "google/gemini-2.0-flash-001"
58
+ api_key_env: "OPENROUTER_API_KEY"
59
+ cost_per_million_input_tokens: 0.10
60
+ cost_per_million_output_tokens: 0.40
61
+ default_params:
62
+ max_tokens: 4096
63
+ temperature: 0.7
64
+
65
+ openrouter-claude-sonnet-4.5:
66
+ type: "openrouter"
67
+ model: "anthropic/claude-sonnet-4.5"
68
+ api_key_env: "OPENROUTER_API_KEY"
69
+ cost_per_million_input_tokens: 3.00
70
+ cost_per_million_output_tokens: 15.00
71
+ default_params:
72
+ max_tokens: 2048
73
+ temperature: 0.7
74
+
75
+ openrouter-qwen-coder-free:
76
+ type: "openrouter"
77
+ model: "qwen/qwen-2.5-coder-32b-instruct:free"
78
+ api_key_env: "OPENROUTER_API_KEY"
79
+ cost_per_million_input_tokens: 0.00
80
+ cost_per_million_output_tokens: 0.00
81
+ default_params:
82
+ max_tokens: 2048
83
+ temperature: 0.2
package/config.yaml ADDED
@@ -0,0 +1,16 @@
1
+ server:
2
+ name: "mcp-delegation-server"
3
+ default_runner: "nemotron-3-super-120b-a12b"
4
+ max_concurrency: 5
5
+ default_timeout_seconds: 60.0
6
+
7
+ # =========================================================================
8
+ # Modular Runner Configurations (Organized by Provider & Purpose)
9
+ # =========================================================================
10
+ includes:
11
+ - "config/runners/agents.yaml" # Specialized Role-Based Agents
12
+ - "config/runners/nvidia.yaml" # NVIDIA NIM / API Catalog Models
13
+ - "config/runners/openrouter.yaml" # OpenRouter Cloud Models
14
+ - "config/runners/ollama.yaml" # Local Offline Models ($0.00)
15
+ - "config/runners/anthropic.yaml" # Anthropic Direct API
16
+ - "config/runners/openai.yaml" # OpenAI Direct API
@@ -0,0 +1,49 @@
1
+ /** Benchmark engine for concurrent multi-runner execution and evaluation. */
2
+ import type { RunnerRegistry } from '../runners/registry.js';
3
+ /** Performance and evaluation scores for a single runner in a benchmark. */
4
+ export interface RunnerBenchmarkSummary {
5
+ runner_id: string;
6
+ model: string;
7
+ success: boolean;
8
+ latency_ms: number;
9
+ input_tokens: number;
10
+ output_tokens: number;
11
+ estimated_cost_usd: number;
12
+ scores: Record<string, Record<string, unknown>>;
13
+ overall_score: number;
14
+ output_preview: string;
15
+ full_output: string;
16
+ error_message: string | null;
17
+ }
18
+ /** Consolidated benchmark report across all participating runners. */
19
+ export interface BenchmarkResult {
20
+ task: string;
21
+ timestamp: string;
22
+ total_duration_ms: number;
23
+ winner_runner_id: string | null;
24
+ runners_evaluated: number;
25
+ successful_runs: number;
26
+ failed_runs: number;
27
+ summaries: RunnerBenchmarkSummary[];
28
+ comparison_table_markdown: string;
29
+ }
30
+ /** Serialize a benchmark report the way `BenchmarkResult.to_json()` did. */
31
+ export declare function benchmarkResultToJson(result: BenchmarkResult, indent?: number | null): string;
32
+ export interface RunBenchmarkOptions {
33
+ task: string;
34
+ runnerIds: string[];
35
+ params?: Record<string, unknown> | null;
36
+ evalCriteria?: Record<string, unknown> | null;
37
+ referenceAnswer?: string | null;
38
+ judgeRunnerId?: string | null;
39
+ }
40
+ /** Coordinates concurrent runner execution, applies scorers, and aggregates results. */
41
+ export declare class BenchmarkEngine {
42
+ readonly registry: RunnerRegistry;
43
+ readonly maxConcurrency: number;
44
+ constructor(registry: RunnerRegistry, maxConcurrency?: number);
45
+ /** Fan a task out across multiple runners concurrently and evaluate their outputs. */
46
+ runBenchmark(options: RunBenchmarkOptions): Promise<BenchmarkResult>;
47
+ /** Construct a GitHub-flavored Markdown comparison table. */
48
+ private buildMarkdownTable;
49
+ }
@@ -0,0 +1,147 @@
1
+ /** Benchmark engine for concurrent multi-runner execution and evaluation. */
2
+ import { CostScorer, ExactMatchScorer, LatencyScorer, LLMJudgeScorer, } from './scorers.js';
3
+ import { makeRunnerResult } from '../runners/base.js';
4
+ import { errorMessage, round, Semaphore, startTimer, truncateByCodePoint } from '../util.js';
5
+ /** Serialize a benchmark report the way `BenchmarkResult.to_json()` did. */
6
+ export function benchmarkResultToJson(result, indent = 2) {
7
+ return JSON.stringify(result, null, indent ?? undefined);
8
+ }
9
+ /** Coordinates concurrent runner execution, applies scorers, and aggregates results. */
10
+ export class BenchmarkEngine {
11
+ registry;
12
+ maxConcurrency;
13
+ constructor(registry, maxConcurrency = 5) {
14
+ this.registry = registry;
15
+ this.maxConcurrency = maxConcurrency;
16
+ }
17
+ /** Fan a task out across multiple runners concurrently and evaluate their outputs. */
18
+ async runBenchmark(options) {
19
+ const { task, runnerIds, params, evalCriteria, referenceAnswer, judgeRunnerId } = options;
20
+ const elapsed = startTimer();
21
+ const timestamp = new Date().toISOString();
22
+ const criteria = evalCriteria ?? {};
23
+ // Unknown runner IDs are silently skipped, as in the Python original.
24
+ const runnersToExecute = [];
25
+ for (const runnerId of runnerIds) {
26
+ const runner = this.registry.get(runnerId);
27
+ if (runner)
28
+ runnersToExecute.push(runner);
29
+ }
30
+ // 1. Execute all runners concurrently, bounded by max_concurrency.
31
+ const semaphore = new Semaphore(this.maxConcurrency);
32
+ const rawResults = await Promise.all(runnersToExecute.map((runner) => semaphore.run(async () => {
33
+ try {
34
+ return await runner.execute(task, params);
35
+ }
36
+ catch (error) {
37
+ return makeRunnerResult({
38
+ runner_id: runner.id,
39
+ model: runner.model,
40
+ task,
41
+ success: false,
42
+ error_message: `Execution failed: ${errorMessage(error)}`,
43
+ });
44
+ }
45
+ })));
46
+ // 2. Build scorers.
47
+ const scorers = [new LatencyScorer(), new CostScorer()];
48
+ if (referenceAnswer)
49
+ scorers.push(new ExactMatchScorer());
50
+ if (judgeRunnerId) {
51
+ const judgeRunner = this.registry.get(judgeRunnerId);
52
+ if (judgeRunner) {
53
+ const rubric = typeof criteria['rubric'] === 'string' ? criteria['rubric'] : null;
54
+ scorers.push(new LLMJudgeScorer(judgeRunner, rubric));
55
+ }
56
+ }
57
+ // 3. Score every result.
58
+ const summaries = [];
59
+ for (const result of rawResults) {
60
+ const scores = {};
61
+ const qualityScores = [];
62
+ for (const scorer of scorers) {
63
+ try {
64
+ const scoreResult = await scorer.score({
65
+ task,
66
+ result,
67
+ referenceAnswer,
68
+ allResults: rawResults,
69
+ });
70
+ scores[scorer.name] = scoreResult;
71
+ if (scorer instanceof LLMJudgeScorer) {
72
+ qualityScores.push(scoreResult.score);
73
+ }
74
+ else if (scorer instanceof ExactMatchScorer && referenceAnswer) {
75
+ qualityScores.push(scoreResult.score);
76
+ }
77
+ }
78
+ catch (error) {
79
+ scores[scorer.name] = {
80
+ scorer_name: scorer.name,
81
+ score: 0,
82
+ error: errorMessage(error),
83
+ };
84
+ }
85
+ }
86
+ // Overall score: mean of the quality scores, else 100 on success and 0 on failure.
87
+ let overall;
88
+ if (qualityScores.length > 0) {
89
+ overall = round(qualityScores.reduce((sum, value) => sum + value, 0) / qualityScores.length, 1);
90
+ }
91
+ else {
92
+ overall = result.success ? 100 : 0;
93
+ }
94
+ summaries.push({
95
+ runner_id: result.runner_id,
96
+ model: result.model,
97
+ success: result.success,
98
+ latency_ms: result.latency_ms,
99
+ input_tokens: result.input_tokens,
100
+ output_tokens: result.output_tokens,
101
+ estimated_cost_usd: result.estimated_cost_usd,
102
+ scores,
103
+ overall_score: overall,
104
+ output_preview: truncateByCodePoint(result.output, 150),
105
+ full_output: result.output,
106
+ error_message: result.error_message,
107
+ });
108
+ }
109
+ // 4. Rank: successes first, then higher score, then lower latency.
110
+ summaries.sort((a, b) => Number(b.success) - Number(a.success) ||
111
+ b.overall_score - a.overall_score ||
112
+ a.latency_ms - b.latency_ms);
113
+ const winner = summaries[0];
114
+ const winnerId = winner && winner.success ? winner.runner_id : null;
115
+ const successfulCount = summaries.filter((summary) => summary.success).length;
116
+ return {
117
+ task,
118
+ timestamp,
119
+ total_duration_ms: elapsed(),
120
+ winner_runner_id: winnerId,
121
+ runners_evaluated: summaries.length,
122
+ successful_runs: successfulCount,
123
+ failed_runs: summaries.length - successfulCount,
124
+ summaries,
125
+ comparison_table_markdown: this.buildMarkdownTable(summaries, winnerId),
126
+ };
127
+ }
128
+ /** Construct a GitHub-flavored Markdown comparison table. */
129
+ buildMarkdownTable(summaries, winnerId) {
130
+ const lines = [
131
+ '| Rank | Runner ID | Model | Status | Latency | Tokens (In/Out) | Est. Cost | Quality Score |',
132
+ '| :--- | :--- | :--- | :---: | :---: | :---: | :---: | :---: |',
133
+ ];
134
+ summaries.forEach((summary, index) => {
135
+ const trophy = summary.runner_id === winnerId ? ' 🏆 (Winner)' : '';
136
+ const status = summary.success ? '✅ Success' : '❌ Failed';
137
+ const tokens = `${summary.input_tokens} / ${summary.output_tokens}`;
138
+ const cost = `$${summary.estimated_cost_usd.toFixed(5)}`;
139
+ const latency = `${summary.latency_ms.toFixed(0)} ms`;
140
+ const score = summary.success ? `${summary.overall_score.toFixed(1)}/100` : 'N/A';
141
+ lines.push(`| ${index + 1} | **\`${summary.runner_id}\`**${trophy} | \`${summary.model}\` | ${status} | ` +
142
+ `${latency} | ${tokens} | ${cost} | ${score} |`);
143
+ });
144
+ return lines.join('\n');
145
+ }
146
+ }
147
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/benchmark/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /** Pluggable scorers for benchmarking LLM outputs. */
2
+ import type { BaseRunner, RunnerResult } from '../runners/base.js';
3
+ /**
4
+ * Evaluation score and metrics returned by a scorer.
5
+ *
6
+ * snake_case and in declaration order: this is serialized straight onto the wire.
7
+ */
8
+ export interface ScoreResult {
9
+ scorer_name: string;
10
+ /** Normalized score (typically 0-100) or a raw metric value. */
11
+ score: number;
12
+ /** "points", "ms", "usd", "percent", "tokens/sec". */
13
+ unit: string;
14
+ details: Record<string, unknown>;
15
+ reasoning: string | null;
16
+ }
17
+ /** Context passed to every scorer. */
18
+ export interface ScoreContext {
19
+ task: string;
20
+ result: RunnerResult;
21
+ referenceAnswer?: string | null;
22
+ allResults?: RunnerResult[] | null;
23
+ }
24
+ /** Abstract base class for benchmark scorers. */
25
+ export declare abstract class BaseScorer {
26
+ abstract get name(): string;
27
+ /** Compute an evaluation score for a runner's result. */
28
+ abstract score(context: ScoreContext): Promise<ScoreResult>;
29
+ }
30
+ /** Scores runners on response latency and generation throughput. */
31
+ export declare class LatencyScorer extends BaseScorer {
32
+ get name(): string;
33
+ score({ result }: ScoreContext): Promise<ScoreResult>;
34
+ }
35
+ /** Scores runners on financial token cost in USD. */
36
+ export declare class CostScorer extends BaseScorer {
37
+ get name(): string;
38
+ score({ result }: ScoreContext): Promise<ScoreResult>;
39
+ }
40
+ /**
41
+ * Scores output against a reference answer via exact match and token overlap.
42
+ *
43
+ * The word split uses `\p{L}\p{N}_` rather than JS's ASCII-only `\w` so the
44
+ * overlap ratio matches Python's Unicode-aware `re.findall(r"\w+", ...)`.
45
+ */
46
+ export declare class ExactMatchScorer extends BaseScorer {
47
+ private static readonly WORD_PATTERN;
48
+ get name(): string;
49
+ score({ result, referenceAnswer }: ScoreContext): Promise<ScoreResult>;
50
+ }
51
+ /** Uses a designated LLM runner as a judge to score outputs against a rubric. */
52
+ export declare class LLMJudgeScorer extends BaseScorer {
53
+ readonly judgeRunner: BaseRunner;
54
+ static readonly DEFAULT_RUBRIC: string;
55
+ readonly rubric: string;
56
+ constructor(judgeRunner: BaseRunner, rubric?: string | null);
57
+ get name(): string;
58
+ score({ task, result, referenceAnswer }: ScoreContext): Promise<ScoreResult>;
59
+ }
@@ -0,0 +1,241 @@
1
+ /** Pluggable scorers for benchmarking LLM outputs. */
2
+ import { errorMessage, round } from '../util.js';
3
+ function makeScore(init) {
4
+ return {
5
+ scorer_name: init.scorer_name,
6
+ score: init.score,
7
+ unit: init.unit ?? 'points',
8
+ details: init.details ?? {},
9
+ reasoning: init.reasoning ?? null,
10
+ };
11
+ }
12
+ /** Abstract base class for benchmark scorers. */
13
+ export class BaseScorer {
14
+ }
15
+ /** Scores runners on response latency and generation throughput. */
16
+ export class LatencyScorer extends BaseScorer {
17
+ get name() {
18
+ return 'latency';
19
+ }
20
+ async score({ result }) {
21
+ if (!result.success) {
22
+ return makeScore({
23
+ scorer_name: this.name,
24
+ score: 0,
25
+ unit: 'ms',
26
+ details: { latency_ms: result.latency_ms, tokens_per_second: 0 },
27
+ reasoning: 'Execution failed',
28
+ });
29
+ }
30
+ let tokensPerSec = 0;
31
+ if (result.latency_ms > 0 && result.output_tokens > 0) {
32
+ tokensPerSec = round(result.output_tokens / (result.latency_ms / 1000), 2);
33
+ }
34
+ return makeScore({
35
+ scorer_name: this.name,
36
+ score: result.latency_ms,
37
+ unit: 'ms',
38
+ details: {
39
+ latency_ms: result.latency_ms,
40
+ output_tokens: result.output_tokens,
41
+ tokens_per_second: tokensPerSec,
42
+ },
43
+ reasoning: `Completed in ${result.latency_ms} ms (~${tokensPerSec} tokens/sec)`,
44
+ });
45
+ }
46
+ }
47
+ /** Scores runners on financial token cost in USD. */
48
+ export class CostScorer extends BaseScorer {
49
+ get name() {
50
+ return 'cost';
51
+ }
52
+ async score({ result }) {
53
+ const totalTokens = result.input_tokens + result.output_tokens;
54
+ return makeScore({
55
+ scorer_name: this.name,
56
+ score: result.estimated_cost_usd,
57
+ unit: 'usd',
58
+ details: {
59
+ input_tokens: result.input_tokens,
60
+ output_tokens: result.output_tokens,
61
+ total_tokens: totalTokens,
62
+ estimated_cost_usd: result.estimated_cost_usd,
63
+ },
64
+ reasoning: `Cost: $${result.estimated_cost_usd.toFixed(6)} across ${totalTokens} total tokens`,
65
+ });
66
+ }
67
+ }
68
+ /**
69
+ * Scores output against a reference answer via exact match and token overlap.
70
+ *
71
+ * The word split uses `\p{L}\p{N}_` rather than JS's ASCII-only `\w` so the
72
+ * overlap ratio matches Python's Unicode-aware `re.findall(r"\w+", ...)`.
73
+ */
74
+ export class ExactMatchScorer extends BaseScorer {
75
+ static WORD_PATTERN = /[\p{L}\p{N}_]+/gu;
76
+ get name() {
77
+ return 'reference_match';
78
+ }
79
+ async score({ result, referenceAnswer }) {
80
+ if (!referenceAnswer) {
81
+ return makeScore({
82
+ scorer_name: this.name,
83
+ score: 0,
84
+ unit: 'percent',
85
+ details: { evaluated: false },
86
+ reasoning: 'No reference answer provided for matching',
87
+ });
88
+ }
89
+ if (!result.success || !result.output) {
90
+ return makeScore({
91
+ scorer_name: this.name,
92
+ score: 0,
93
+ unit: 'percent',
94
+ details: { exact_match: false, token_overlap: 0 },
95
+ reasoning: 'Runner did not produce valid output',
96
+ });
97
+ }
98
+ const target = referenceAnswer.trim();
99
+ const actual = result.output.trim();
100
+ if (actual === target) {
101
+ return makeScore({
102
+ scorer_name: this.name,
103
+ score: 100,
104
+ unit: 'percent',
105
+ details: { exact_match: true, token_overlap: 1 },
106
+ reasoning: 'Exact verbatim match with reference answer',
107
+ });
108
+ }
109
+ if (actual.toLowerCase() === target.toLowerCase()) {
110
+ return makeScore({
111
+ scorer_name: this.name,
112
+ score: 95,
113
+ unit: 'percent',
114
+ details: { exact_match: false, case_insensitive_match: true },
115
+ reasoning: 'Case-insensitive match with reference answer',
116
+ });
117
+ }
118
+ if (actual.toLowerCase().includes(target.toLowerCase())) {
119
+ return makeScore({
120
+ scorer_name: this.name,
121
+ score: 80,
122
+ unit: 'percent',
123
+ details: { substring_match: true },
124
+ reasoning: 'Reference answer is fully contained in output',
125
+ });
126
+ }
127
+ const targetWords = new Set(target.toLowerCase().match(ExactMatchScorer.WORD_PATTERN) ?? []);
128
+ const actualWords = new Set(actual.toLowerCase().match(ExactMatchScorer.WORD_PATTERN) ?? []);
129
+ let overlap = 0;
130
+ if (targetWords.size > 0) {
131
+ let intersection = 0;
132
+ for (const word of targetWords)
133
+ if (actualWords.has(word))
134
+ intersection++;
135
+ overlap = intersection / targetWords.size;
136
+ }
137
+ return makeScore({
138
+ scorer_name: this.name,
139
+ score: round(overlap * 70, 1),
140
+ unit: 'percent',
141
+ details: { exact_match: false, token_overlap_ratio: round(overlap, 3) },
142
+ reasoning: `Token overlap ratio: ${(overlap * 100).toFixed(1)}% with reference answer`,
143
+ });
144
+ }
145
+ }
146
+ /** Uses a designated LLM runner as a judge to score outputs against a rubric. */
147
+ export class LLMJudgeScorer extends BaseScorer {
148
+ judgeRunner;
149
+ static DEFAULT_RUBRIC = 'Evaluate the response on a scale from 0 to 100 based on:\n' +
150
+ '1. Correctness & Accuracy (40%)\n' +
151
+ '2. Completeness & Code Quality (30%)\n' +
152
+ '3. Conciseness & Clarity (20%)\n' +
153
+ '4. Strict adherence to instructions (10%)\n';
154
+ rubric;
155
+ constructor(judgeRunner, rubric) {
156
+ super();
157
+ this.judgeRunner = judgeRunner;
158
+ this.rubric = rubric || LLMJudgeScorer.DEFAULT_RUBRIC;
159
+ }
160
+ get name() {
161
+ return `llm_judge (${this.judgeRunner.id})`;
162
+ }
163
+ async score({ task, result, referenceAnswer }) {
164
+ if (!result.success) {
165
+ return makeScore({
166
+ scorer_name: this.name,
167
+ score: 0,
168
+ unit: 'points',
169
+ details: { judge_model: this.judgeRunner.model },
170
+ reasoning: 'Output failed execution, 0 points assigned',
171
+ });
172
+ }
173
+ const refSection = referenceAnswer ? `\n\n[Optional Reference Answer]:\n${referenceAnswer}` : '';
174
+ const judgePrompt = `You are an impartial, expert AI benchmark judge.
175
+ Your role is to evaluate an LLM's response to a given user task according to the scoring rubric.
176
+
177
+ [Evaluation Rubric]:
178
+ ${this.rubric}
179
+
180
+ [User Task/Prompt]:
181
+ ${task}
182
+ ${refSection}
183
+
184
+ [Candidate Response to Evaluate]:
185
+ ${result.output}
186
+
187
+ Provide your evaluation strictly in valid JSON format with the following keys:
188
+ - "score": A numeric score from 0.0 to 100.0 (float).
189
+ - "strengths": A short list of strengths (strings).
190
+ - "weaknesses": A short list of weaknesses (strings).
191
+ - "critique": A 1-2 sentence overall summary of the evaluation.
192
+
193
+ Respond ONLY with valid JSON, without any markdown code fence wrappers if possible.
194
+ `;
195
+ try {
196
+ const judgeResult = await this.judgeRunner.execute(judgePrompt, { temperature: 0.1, max_tokens: 512 });
197
+ if (!judgeResult.success) {
198
+ return makeScore({
199
+ scorer_name: this.name,
200
+ score: 50,
201
+ unit: 'points',
202
+ details: { error: judgeResult.error_message },
203
+ reasoning: `Judge failed to evaluate: ${judgeResult.error_message}`,
204
+ });
205
+ }
206
+ let cleaned = judgeResult.output.trim();
207
+ if (cleaned.startsWith('```')) {
208
+ cleaned = cleaned.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '');
209
+ }
210
+ const parsed = JSON.parse(cleaned);
211
+ const rawScore = 'score' in parsed ? Number(parsed['score']) : 50;
212
+ if (!Number.isFinite(rawScore))
213
+ throw new Error(`could not convert score to float: ${String(parsed['score'])}`);
214
+ const scoreValue = Math.max(0, Math.min(100, rawScore));
215
+ const critique = 'critique' in parsed ? String(parsed['critique']) : `Judge assigned ${scoreValue}/100`;
216
+ return makeScore({
217
+ scorer_name: this.name,
218
+ score: scoreValue,
219
+ unit: 'points',
220
+ details: {
221
+ judge_runner: this.judgeRunner.id,
222
+ judge_model: this.judgeRunner.model,
223
+ strengths: parsed['strengths'] ?? [],
224
+ weaknesses: parsed['weaknesses'] ?? [],
225
+ critique: 'critique' in parsed ? parsed['critique'] : '',
226
+ },
227
+ reasoning: critique,
228
+ });
229
+ }
230
+ catch (error) {
231
+ return makeScore({
232
+ scorer_name: this.name,
233
+ score: 50,
234
+ unit: 'points',
235
+ details: { parse_error: errorMessage(error) },
236
+ reasoning: `Judge output parsing failed: ${errorMessage(error)}`,
237
+ });
238
+ }
239
+ }
240
+ }
241
+ //# sourceMappingURL=scorers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorers.js","sourceRoot":"","sources":["../../src/benchmark/scorers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ /** Fastify HTTP API for SmartRelay MCP delegation server.
3
+ *
4
+ * Provides an authenticated HTTP interface to all MCP tools, allowing external
5
+ * plugins (MCPHub, etc.) to call SmartRelay over HTTP instead of stdio.
6
+ *
7
+ * Transport: HTTP/JSON with Bearer token auth.
8
+ */
9
+ import { type FastifyInstance, type FastifyReply, type FastifyRequest } from 'fastify';
10
+ import { BenchmarkEngine } from './benchmark/engine.js';
11
+ import { TaskRouter } from './router.js';
12
+ import { RunnerRegistry } from './runners/registry.js';
13
+ export declare function getRegistry(configPath?: string | null): RunnerRegistry;
14
+ export declare function getEngine(): BenchmarkEngine;
15
+ export declare function getRouter(): TaskRouter;
16
+ export declare function verifyApiKey(request: FastifyRequest, reply: FastifyReply): void;
17
+ export declare function dispatchTool(toolName: string, args?: Record<string, unknown>): Promise<unknown>;
18
+ export declare function createHttpServer(): FastifyInstance;
19
+ export declare function runHttpServer(options?: {
20
+ config?: string;
21
+ host?: string;
22
+ port?: number;
23
+ }): Promise<void>;