@databricks/appkit 0.73.0 → 0.74.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.
- package/CLAUDE.md +7 -0
- package/dist/appkit/package.js +1 -1
- package/dist/beta.d.ts +6 -6
- package/dist/beta.js +6 -6
- package/dist/cli/commands/agent/eval.js +82 -21
- package/dist/cli/commands/agent/eval.js.map +1 -1
- package/dist/evals/dataset.d.ts +14 -1
- package/dist/evals/dataset.d.ts.map +1 -1
- package/dist/evals/dataset.js +16 -1
- package/dist/evals/dataset.js.map +1 -1
- package/dist/evals/define-eval.d.ts +4 -2
- package/dist/evals/define-eval.d.ts.map +1 -1
- package/dist/evals/define-eval.js +5 -1
- package/dist/evals/define-eval.js.map +1 -1
- package/dist/evals/discover.d.ts +15 -1
- package/dist/evals/discover.d.ts.map +1 -1
- package/dist/evals/discover.js +26 -2
- package/dist/evals/discover.js.map +1 -1
- package/dist/evals/http-driver.d.ts.map +1 -1
- package/dist/evals/http-driver.js +31 -9
- package/dist/evals/http-driver.js.map +1 -1
- package/dist/evals/index.d.ts +5 -5
- package/dist/evals/index.js +5 -5
- package/dist/evals/report.d.ts +16 -1
- package/dist/evals/report.d.ts.map +1 -1
- package/dist/evals/report.js +64 -2
- package/dist/evals/report.js.map +1 -1
- package/dist/evals/run-eval.d.ts +5 -0
- package/dist/evals/run-eval.d.ts.map +1 -1
- package/dist/evals/run-eval.js +44 -3
- package/dist/evals/run-eval.js.map +1 -1
- package/dist/evals/run-evals.d.ts +32 -3
- package/dist/evals/run-evals.d.ts.map +1 -1
- package/dist/evals/run-evals.js +128 -26
- package/dist/evals/run-evals.js.map +1 -1
- package/dist/evals/types.d.ts +40 -2
- package/dist/evals/types.d.ts.map +1 -1
- package/dist/plugins/server/index.js +2 -2
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js +3 -3
- package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
- package/dist/plugins/server/static-server.js +3 -3
- package/dist/plugins/server/static-server.js.map +1 -1
- package/dist/plugins/server/utils.js +3 -3
- package/dist/plugins/server/utils.js.map +1 -1
- package/dist/plugins/server/vite-dev-server.js +4 -4
- package/dist/plugins/server/vite-dev-server.js.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +54 -54
- package/dist/type-generator/database/generate.js +3 -3
- package/dist/type-generator/database/generate.js.map +1 -1
- package/dist/type-generator/migration.js +2 -2
- package/dist/type-generator/migration.js.map +1 -1
- package/dist/type-generator/serving/server-file-extractor.js +3 -3
- package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
- package/docs/api/appkit/Function.defineEvalConfig.md +18 -0
- package/docs/api/appkit/Function.discoverEvalConfigs.md +18 -0
- package/docs/api/appkit/Function.formatResultsJUnit.md +18 -0
- package/docs/api/appkit/Function.formatResultsJson.md +18 -0
- package/docs/api/appkit/Function.runWithRetries.md +28 -0
- package/docs/api/appkit/Function.userTurns.md +20 -0
- package/docs/api/appkit/Interface.DiscoveredEvalConfig.md +25 -0
- package/docs/api/appkit/Interface.DriveResult.md +28 -0
- package/docs/api/appkit/Interface.EvalDefinition.md +22 -0
- package/docs/api/appkit/Interface.EvalDriver.md +10 -4
- package/docs/api/appkit/Interface.EvalResult.md +11 -0
- package/docs/api/appkit/Interface.EvalSummary.md +11 -0
- package/docs/api/appkit/Interface.RunEvalOptions.md +11 -0
- package/docs/api/appkit/Interface.RunEvalsOptions.md +23 -1
- package/docs/api/appkit/Interface.TestContext.md +30 -8
- package/docs/api/appkit.md +7 -0
- package/llms.txt +7 -0
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -99,9 +99,11 @@ npx @databricks/appkit docs <query>
|
|
|
99
99
|
- [Function: createWorkspaceClient()](./docs/api/appkit/Function.createWorkspaceClient.md): Construct an AppKit workspace client.
|
|
100
100
|
- [Function: database()](./docs/api/appkit/Function.database.md): Create a typed database plugin registration for a finalized schema.
|
|
101
101
|
- [Function: defineEval()](./docs/api/appkit/Function.defineEval.md): Define an agent eval. Default-export the result from a
|
|
102
|
+
- [Function: defineEvalConfig()](./docs/api/appkit/Function.defineEvalConfig.md): Define per-directory eval config. Default-export from evals.config.ts.
|
|
102
103
|
- [Function: defineManifest()](./docs/api/appkit/Function.defineManifest.md): Validates a raw manifest (typically a manifest.json import) against the
|
|
103
104
|
- [Function: defineSchema()](./docs/api/appkit/Function.defineSchema.md): Compile one declared schema. The returned type keeps the table names the
|
|
104
105
|
- [Function: defineTool()](./docs/api/appkit/Function.defineTool.md): Defines a single tool entry for a plugin's internal registry.
|
|
106
|
+
- [Function: discoverEvalConfigs()](./docs/api/appkit/Function.discoverEvalConfigs.md): Discover the per-agent evals.config.ts (from defineEvalConfig) at
|
|
105
107
|
- [Function: discoverEvalFiles()](./docs/api/appkit/Function.discoverEvalFiles.md): Discover evals under /server/agents//evals/ — co-located
|
|
106
108
|
- [Function: enumColumn()](./docs/api/appkit/Function.enumColumn.md): Parameters
|
|
107
109
|
- [Function: equals()](./docs/api/appkit/Function.equals.md): Passes when the value equals expected exactly.
|
|
@@ -113,6 +115,8 @@ npx @databricks/appkit docs <query>
|
|
|
113
115
|
- [Function: formatEvalDetail()](./docs/api/appkit/Function.formatEvalDetail.md): Indented detail lines for a failing eval (error + failing assertions).
|
|
114
116
|
- [Function: formatEvalHeadline()](./docs/api/appkit/Function.formatEvalHeadline.md): The one-line header for a single eval result (no failure detail).
|
|
115
117
|
- [Function: formatEvalResults()](./docs/api/appkit/Function.formatEvalResults.md): Render all results as a human-readable console report (non-streaming).
|
|
118
|
+
- [Function: formatResultsJson()](./docs/api/appkit/Function.formatResultsJson.md): Render results as a machine-readable JSON report (2-space indented):
|
|
119
|
+
- [Function: formatResultsJUnit()](./docs/api/appkit/Function.formatResultsJUnit.md): Render results as JUnit XML for standard CI test reporters: a single
|
|
116
120
|
- [Function: formatSummaryLine()](./docs/api/appkit/Function.formatSummaryLine.md): The final PASS/FAIL summary line.
|
|
117
121
|
- [Function: fromSupervisorApi()](./docs/api/appkit/Function.fromSupervisorApi.md): Creates an AgentAdapter backed by the Databricks AI Gateway
|
|
118
122
|
- [Function: functionToolToDefinition()](./docs/api/appkit/Function.functionToolToDefinition.md): Parameters
|
|
@@ -148,11 +152,13 @@ npx @databricks/appkit docs <query>
|
|
|
148
152
|
- [Function: runAgent()](./docs/api/appkit/Function.runAgent.md): Standalone agent execution without createApp. Resolves the adapter, binds
|
|
149
153
|
- [Function: runEval()](./docs/api/appkit/Function.runEval.md): Run a single eval against a driver. Never throws for assertion or agent
|
|
150
154
|
- [Function: runEvalsInDir()](./docs/api/appkit/Function.runEvalsInDir.md): Discover, load, and run every eval under each agent's evals/ dir, driving
|
|
155
|
+
- [Function: runWithRetries()](./docs/api/appkit/Function.runWithRetries.md): Run attempt up to 1 + retries times, stopping as soon as it returns a
|
|
151
156
|
- [Function: summarize()](./docs/api/appkit/Function.summarize.md): Parameters
|
|
152
157
|
- [Function: text()](./docs/api/appkit/Function.text.md): Returns
|
|
153
158
|
- [Function: timestamp()](./docs/api/appkit/Function.timestamp.md): Parameters
|
|
154
159
|
- [Function: tool()](./docs/api/appkit/Function.tool.md): Factory for defining function tools with Zod schemas.
|
|
155
160
|
- [Function: toolsFromRegistry()](./docs/api/appkit/Function.toolsFromRegistry.md): Produces the AgentToolDefinition[] a ToolProvider exposes to the LLM,
|
|
161
|
+
- [Function: userTurns()](./docs/api/appkit/Function.userTurns.md): Extract every user-message content, in order, from an MLflow
|
|
156
162
|
- [Function: uuid()](./docs/api/appkit/Function.uuid.md): Returns
|
|
157
163
|
- [Function: varchar()](./docs/api/appkit/Function.varchar.md): Parameters
|
|
158
164
|
- [Interface: AgentAdapter](./docs/api/appkit/Interface.AgentAdapter.md): Properties
|
|
@@ -174,6 +180,7 @@ npx @databricks/appkit docs <query>
|
|
|
174
180
|
- [Interface: DatabricksAuth](./docs/api/appkit/Interface.DatabricksAuth.md): Resolved Databricks host + bearer token for the eval runner's REST calls.
|
|
175
181
|
- [Interface: DatasetRow](./docs/api/appkit/Interface.DatasetRow.md): One row of a managed evaluation dataset. inputs are the kwargs passed to the
|
|
176
182
|
- [Interface: DiscoveredEval](./docs/api/appkit/Interface.DiscoveredEval.md): An eval file found under server/agents//evals/.
|
|
183
|
+
- [Interface: DiscoveredEvalConfig](./docs/api/appkit/Interface.DiscoveredEvalConfig.md): A per-agent evals.config.ts found under server/agents//evals/.
|
|
177
184
|
- [Interface: DriveResult](./docs/api/appkit/Interface.DriveResult.md): What a driver returns for a single t.send.
|
|
178
185
|
- [Interface: EndpointConfig](./docs/api/appkit/Interface.EndpointConfig.md): Properties
|
|
179
186
|
- [Interface: EntityMutationHooks<TTable>](./docs/api/appkit/Interface.EntityMutationHooks.md): Mutation lifecycle for one entity. A before hook may return a replacement
|
package/dist/appkit/package.js
CHANGED
package/dist/beta.d.ts
CHANGED
|
@@ -17,17 +17,17 @@ import { defineSchema } from "./database/schema-builder/define-schema.js";
|
|
|
17
17
|
import { fk } from "./database/schema-builder/fk.js";
|
|
18
18
|
import { DatabricksAuth, ResolveDatabricksAuthOptions, resolveDatabricksAuth, resolveWorkspaceClient } from "./connectors/mlflow/auth.js";
|
|
19
19
|
import { MlflowClient, PostResult, normalizeHost } from "./connectors/mlflow/client.js";
|
|
20
|
-
import { DatasetRow, ReadEvalDatasetOptions, readEvalDataset } from "./evals/dataset.js";
|
|
20
|
+
import { DatasetRow, ReadEvalDatasetOptions, readEvalDataset, userTurns } from "./evals/dataset.js";
|
|
21
21
|
import { AssertionHandle, AssertionResult, CustomJudgeSpec, DriveResult, EvalDefinition, EvalDriver, EvalResult, MatchResult, Matcher, Severity, TestContext } from "./evals/types.js";
|
|
22
|
-
import { defineEval } from "./evals/define-eval.js";
|
|
23
|
-
import { DiscoveredEval, discoverEvalFiles } from "./evals/discover.js";
|
|
22
|
+
import { defineEval, defineEvalConfig } from "./evals/define-eval.js";
|
|
23
|
+
import { DiscoveredEval, DiscoveredEvalConfig, discoverEvalConfigs, discoverEvalFiles } from "./evals/discover.js";
|
|
24
24
|
import { HttpDriverOptions, createHttpDriver } from "./evals/http-driver.js";
|
|
25
25
|
import { JudgeConfig, JudgeScore, configureJudge, isJudgeConfigured } from "./evals/judge.js";
|
|
26
26
|
import { equals, includes, matches } from "./evals/matchers.js";
|
|
27
27
|
import { Assessment, ReportOutcome, buildAssessments, reportToMlflow } from "./evals/mlflow-report.js";
|
|
28
|
-
import { EvalSummary, evalGlyph, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatSummaryLine, summarize } from "./evals/report.js";
|
|
28
|
+
import { EvalSummary, evalGlyph, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatResultsJUnit, formatResultsJson, formatSummaryLine, summarize } from "./evals/report.js";
|
|
29
29
|
import { RunEvalOptions, runEval } from "./evals/run-eval.js";
|
|
30
|
-
import { EvalProgress, EvalRunSummary, RunEvalsOptions, runEvalsInDir } from "./evals/run-evals.js";
|
|
30
|
+
import { EvalProgress, EvalRunSummary, RunEvalsOptions, runEvalsInDir, runWithRetries } from "./evals/run-evals.js";
|
|
31
31
|
import "./evals/index.js";
|
|
32
32
|
import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "./core/agent/load-agents.js";
|
|
33
33
|
import { agents } from "./plugins/agents/agents.js";
|
|
@@ -40,4 +40,4 @@ import { DatabaseApiConfig, DatabaseApiWriteOperation, DatabaseApiWritesConfig,
|
|
|
40
40
|
import { database } from "./plugins/database/database.js";
|
|
41
41
|
import "./plugins/database/index.js";
|
|
42
42
|
import "./plugins/beta-exports.generated.js";
|
|
43
|
-
export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, AssertionHandle, AssertionResult, Assessment, type AutoInheritToolsConfig, type BaseSystemPromptOption, CustomJudgeSpec, type DatabaseApiConfig, type DatabaseApiWriteOperation, type DatabaseApiWritesConfig, type DatabaseExports, DatabricksAdapter, DatabricksAuth, DatasetRow, DiscoveredEval, DriveResult, type EntityHooks, type EntityMutationHooks, EvalDefinition, EvalDriver, EvalProgress, EvalResult, EvalRunSummary, EvalSummary, type FunctionTool, type GenerationParams, type HookApp, type HookContext, type HostedSupervisorTool, type HostedTool, HttpDriverOptions, type IAiSearchConfig, type IDatabaseConfig, type IndexConfig, JudgeConfig, JudgeScore, MatchResult, Matcher, type McpConnectAllResult, type Message, MlflowClient, type PluginToolkitProvider, type Plugins, PostResult, type PromptContext, ReadEvalDatasetOptions, type ReadSerializer, type ReadSerializerContext, type RegisteredAgent, ReportOutcome, type RerankerConfig, ResolveDatabricksAuthOptions, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, RunEvalOptions, RunEvalsOptions, SUPERVISOR_EXTENSION_KEY, type Schema, type SearchFilters, type SearchRequest, type SearchResponse, type SearchResult, Severity, SupervisorApiAdapter, type SupervisorApiAdapterOptions, type SupervisorExtension, type SupervisorTool, TestContext, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, type TransactionClient, type WorkspaceClientLike, agentIdFromMarkdownPath, agents, aiSearch, bigid, bigint, boolean, buildAssessments, configureJudge, createAgent, createHttpDriver, database, defineEval, defineSchema, defineTool, discoverEvalFiles, enumColumn, equals, evalGlyph, executeFromRegistry, fk, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatSummaryLine, fromSupervisorApi, functionToolToDefinition, id, includes, integer, isFunctionTool, isHostedTool, isJudgeConfigured, isSupervisorTool, isToolkitEntry, jsonb, loadAgentFromFile, loadAgentsFromDir, matches, mcpServer, normalizeHost, parseTextToolCalls, readEvalDataset, reportToMlflow, resolveDatabricksAuth, resolveHostedTools, resolveWorkspaceClient, runAgent, runEval, runEvalsInDir, summarize, supervisorTools, text, timestamp, tool, toolsFromRegistry, uuid, varchar };
|
|
43
|
+
export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, AssertionHandle, AssertionResult, Assessment, type AutoInheritToolsConfig, type BaseSystemPromptOption, CustomJudgeSpec, type DatabaseApiConfig, type DatabaseApiWriteOperation, type DatabaseApiWritesConfig, type DatabaseExports, DatabricksAdapter, DatabricksAuth, DatasetRow, DiscoveredEval, DiscoveredEvalConfig, DriveResult, type EntityHooks, type EntityMutationHooks, EvalDefinition, EvalDriver, EvalProgress, EvalResult, EvalRunSummary, EvalSummary, type FunctionTool, type GenerationParams, type HookApp, type HookContext, type HostedSupervisorTool, type HostedTool, HttpDriverOptions, type IAiSearchConfig, type IDatabaseConfig, type IndexConfig, JudgeConfig, JudgeScore, MatchResult, Matcher, type McpConnectAllResult, type Message, MlflowClient, type PluginToolkitProvider, type Plugins, PostResult, type PromptContext, ReadEvalDatasetOptions, type ReadSerializer, type ReadSerializerContext, type RegisteredAgent, ReportOutcome, type RerankerConfig, ResolveDatabricksAuthOptions, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, RunEvalOptions, RunEvalsOptions, SUPERVISOR_EXTENSION_KEY, type Schema, type SearchFilters, type SearchRequest, type SearchResponse, type SearchResult, Severity, SupervisorApiAdapter, type SupervisorApiAdapterOptions, type SupervisorExtension, type SupervisorTool, TestContext, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, type TransactionClient, type WorkspaceClientLike, agentIdFromMarkdownPath, agents, aiSearch, bigid, bigint, boolean, buildAssessments, configureJudge, createAgent, createHttpDriver, database, defineEval, defineEvalConfig, defineSchema, defineTool, discoverEvalConfigs, discoverEvalFiles, enumColumn, equals, evalGlyph, executeFromRegistry, fk, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatResultsJUnit, formatResultsJson, formatSummaryLine, fromSupervisorApi, functionToolToDefinition, id, includes, integer, isFunctionTool, isHostedTool, isJudgeConfigured, isSupervisorTool, isToolkitEntry, jsonb, loadAgentFromFile, loadAgentsFromDir, matches, mcpServer, normalizeHost, parseTextToolCalls, readEvalDataset, reportToMlflow, resolveDatabricksAuth, resolveHostedTools, resolveWorkspaceClient, runAgent, runEval, runEvalsInDir, runWithRetries, summarize, supervisorTools, text, timestamp, tool, toolsFromRegistry, userTurns, uuid, varchar };
|
package/dist/beta.js
CHANGED
|
@@ -15,16 +15,16 @@ import { isToolkitEntry } from "./core/agent/types.js";
|
|
|
15
15
|
import { runAgent } from "./core/agent/run-agent.js";
|
|
16
16
|
import "./core/agent/tools/index.js";
|
|
17
17
|
import "./database/schema-builder/index.js";
|
|
18
|
-
import { readEvalDataset } from "./evals/dataset.js";
|
|
19
|
-
import { defineEval } from "./evals/define-eval.js";
|
|
20
|
-
import { discoverEvalFiles } from "./evals/discover.js";
|
|
18
|
+
import { readEvalDataset, userTurns } from "./evals/dataset.js";
|
|
19
|
+
import { defineEval, defineEvalConfig } from "./evals/define-eval.js";
|
|
20
|
+
import { discoverEvalConfigs, discoverEvalFiles } from "./evals/discover.js";
|
|
21
21
|
import { createHttpDriver } from "./evals/http-driver.js";
|
|
22
22
|
import { configureJudge, isJudgeConfigured } from "./evals/judge.js";
|
|
23
23
|
import { equals, includes, matches } from "./evals/matchers.js";
|
|
24
24
|
import { buildAssessments, reportToMlflow } from "./evals/mlflow-report.js";
|
|
25
|
-
import { evalGlyph, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatSummaryLine, summarize } from "./evals/report.js";
|
|
25
|
+
import { evalGlyph, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatResultsJUnit, formatResultsJson, formatSummaryLine, summarize } from "./evals/report.js";
|
|
26
26
|
import { runEval } from "./evals/run-eval.js";
|
|
27
|
-
import { runEvalsInDir } from "./evals/run-evals.js";
|
|
27
|
+
import { runEvalsInDir, runWithRetries } from "./evals/run-evals.js";
|
|
28
28
|
import "./evals/index.js";
|
|
29
29
|
import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "./core/agent/load-agents.js";
|
|
30
30
|
import { agents } from "./plugins/agents/agents.js";
|
|
@@ -33,4 +33,4 @@ import { aiSearch } from "./plugins/ai-search/ai-search.js";
|
|
|
33
33
|
import { database } from "./plugins/database/database.js";
|
|
34
34
|
import "./plugins/beta-exports.generated.js";
|
|
35
35
|
|
|
36
|
-
export { AppKitMcpClient, DatabricksAdapter, MlflowClient, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, agentIdFromMarkdownPath, agents, aiSearch, bigid, bigint, boolean, buildAssessments, configureJudge, createAgent, createHttpDriver, database, defineEval, defineSchema, defineTool, discoverEvalFiles, enumColumn, equals, evalGlyph, executeFromRegistry, fk, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatSummaryLine, fromSupervisorApi, functionToolToDefinition, id, includes, integer, isFunctionTool, isHostedTool, isJudgeConfigured, isSupervisorTool, isToolkitEntry, jsonb, loadAgentFromFile, loadAgentsFromDir, matches, mcpServer, normalizeHost, parseTextToolCalls, readEvalDataset, reportToMlflow, resolveDatabricksAuth, resolveHostedTools, resolveWorkspaceClient, runAgent, runEval, runEvalsInDir, summarize, supervisorTools, text, timestamp, tool, toolsFromRegistry, uuid, varchar };
|
|
36
|
+
export { AppKitMcpClient, DatabricksAdapter, MlflowClient, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, agentIdFromMarkdownPath, agents, aiSearch, bigid, bigint, boolean, buildAssessments, configureJudge, createAgent, createHttpDriver, database, defineEval, defineEvalConfig, defineSchema, defineTool, discoverEvalConfigs, discoverEvalFiles, enumColumn, equals, evalGlyph, executeFromRegistry, fk, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatResultsJUnit, formatResultsJson, formatSummaryLine, fromSupervisorApi, functionToolToDefinition, id, includes, integer, isFunctionTool, isHostedTool, isJudgeConfigured, isSupervisorTool, isToolkitEntry, jsonb, loadAgentFromFile, loadAgentsFromDir, matches, mcpServer, normalizeHost, parseTextToolCalls, readEvalDataset, reportToMlflow, resolveDatabricksAuth, resolveHostedTools, resolveWorkspaceClient, runAgent, runEval, runEvalsInDir, runWithRetries, summarize, supervisorTools, text, timestamp, tool, toolsFromRegistry, userTurns, uuid, varchar };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { Command, Option } from "commander";
|
|
2
3
|
|
|
3
4
|
//#region src/cli/commands/agent/eval.ts
|
|
4
5
|
/**
|
|
@@ -24,6 +25,17 @@ function parseHeaders(values) {
|
|
|
24
25
|
return headers;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
28
|
+
* Parse `--min-pass-rate`: a finite number in `[0, 1]`, or `undefined` when
|
|
29
|
+
* unset. Throws on blank, out-of-range, or non-numeric input so a bad gate
|
|
30
|
+
* value fails fast instead of silently disabling or inverting the CI gate.
|
|
31
|
+
*/
|
|
32
|
+
function parsePassRate(raw) {
|
|
33
|
+
if (raw === void 0) return void 0;
|
|
34
|
+
const n = Number(raw);
|
|
35
|
+
if (raw.trim() === "" || !Number.isFinite(n) || n < 0 || n > 1) throw new Error(`Invalid --min-pass-rate "${raw}" — expected a number in [0, 1]`);
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
27
39
|
* Native MLflow "Evaluation run" config — only when creds + an experiment are
|
|
28
40
|
* all present (traces live in the app; the run + scores are driven from here).
|
|
29
41
|
*/
|
|
@@ -47,17 +59,22 @@ function resolveJudge(opts, auth) {
|
|
|
47
59
|
model
|
|
48
60
|
} : void 0;
|
|
49
61
|
}
|
|
50
|
-
/**
|
|
51
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Progress reporter: stream each eval as it runs instead of going silent. In a
|
|
64
|
+
* machine reporter (json/junit) the live per-eval streaming is suppressed and
|
|
65
|
+
* banners go to stderr (via `info`), keeping stdout clean for the report.
|
|
66
|
+
*/
|
|
67
|
+
function makeProgressReporter(runner, url, machine, info) {
|
|
52
68
|
return (event) => {
|
|
53
69
|
switch (event.type) {
|
|
54
70
|
case "discovered":
|
|
55
|
-
|
|
71
|
+
info(`Running ${event.total} eval${event.total === 1 ? "" : "s"} against ${url}\n`);
|
|
56
72
|
break;
|
|
57
73
|
case "run-created":
|
|
58
|
-
|
|
74
|
+
info(`MLflow evaluation run: ${event.runId}\n`);
|
|
59
75
|
break;
|
|
60
76
|
case "result":
|
|
77
|
+
if (machine) break;
|
|
61
78
|
console.log(`[${event.index + 1}/${event.total}] ${runner.formatEvalHeadline(event.result)}`);
|
|
62
79
|
for (const line of runner.formatEvalDetail(event.result)) console.log(line);
|
|
63
80
|
break;
|
|
@@ -67,33 +84,53 @@ function makeProgressReporter(runner, url) {
|
|
|
67
84
|
function formatFailureLine(f) {
|
|
68
85
|
return ` ✗ trace ${f.traceId}: ${f.status ?? ""} ${f.error ?? ""}`.trim();
|
|
69
86
|
}
|
|
70
|
-
/**
|
|
71
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Print the MLflow assessment/finish outcome after a run that created one. The
|
|
89
|
+
* summary line goes through `info` (stderr under a machine reporter); per-trace
|
|
90
|
+
* failures and finish errors always go to stderr.
|
|
91
|
+
*/
|
|
92
|
+
function printMlflowOutcome(mlflow, info) {
|
|
72
93
|
const { report, finish } = mlflow;
|
|
73
|
-
|
|
94
|
+
info(`MLflow: ${report.written} assessment(s) written` + (report.skipped ? `, ${report.skipped} skipped` : "") + (report.failures.length ? `, ${report.failures.length} failed` : ""));
|
|
74
95
|
for (const f of report.failures) console.error(formatFailureLine(f));
|
|
75
96
|
if (finish.metricsError) console.error(` ⚠ metrics not logged: ${finish.metricsError}`);
|
|
76
97
|
if (!finish.finished) console.error(` ✗ run left RUNNING — failed to finish: ${finish.finishError ?? "unknown"}`);
|
|
77
98
|
}
|
|
78
99
|
async function runAgentEval(filter, opts) {
|
|
79
100
|
const runner = await loadRunner();
|
|
80
|
-
const
|
|
101
|
+
const credentials = {
|
|
81
102
|
profile: opts.profile ?? process.env.DATABRICKS_CONFIG_PROFILE,
|
|
82
103
|
host: opts.databricksHost ?? process.env.DATABRICKS_HOST,
|
|
83
104
|
token: opts.databricksToken ?? process.env.DATABRICKS_TOKEN
|
|
84
|
-
}
|
|
105
|
+
};
|
|
106
|
+
const auth = await runner.resolveDatabricksAuth(credentials) ?? {};
|
|
85
107
|
const warehouseId = opts.warehouseId ?? process.env.DATABRICKS_WAREHOUSE_ID;
|
|
86
|
-
const workspaceClient = runner.resolveWorkspaceClient(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
108
|
+
const workspaceClient = runner.resolveWorkspaceClient(credentials);
|
|
109
|
+
const parsedTimeout = opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0;
|
|
110
|
+
const timeoutMs = parsedTimeout && parsedTimeout > 0 ? parsedTimeout : void 0;
|
|
111
|
+
const parsedRetries = opts.retries ? Number.parseInt(opts.retries, 10) : void 0;
|
|
112
|
+
const retries = parsedRetries && parsedRetries > 0 ? parsedRetries : void 0;
|
|
113
|
+
let minPassRate;
|
|
114
|
+
try {
|
|
115
|
+
minPassRate = parsePassRate(opts.minPassRate);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const reporter = opts.reporter ?? "text";
|
|
122
|
+
const machine = reporter !== "text";
|
|
123
|
+
const info = (msg) => {
|
|
124
|
+
if (machine) console.error(msg);
|
|
125
|
+
else console.log(msg);
|
|
126
|
+
};
|
|
91
127
|
let summary;
|
|
92
128
|
try {
|
|
93
129
|
summary = await runner.runEvalsInDir({
|
|
94
130
|
rootDir: opts.root,
|
|
95
131
|
baseUrl: opts.url,
|
|
96
132
|
filter,
|
|
133
|
+
tags: opts.tag,
|
|
97
134
|
strict: opts.strict,
|
|
98
135
|
headers: opts.header ? parseHeaders(opts.header) : void 0,
|
|
99
136
|
concurrency: opts.concurrency,
|
|
@@ -101,19 +138,43 @@ async function runAgentEval(filter, opts) {
|
|
|
101
138
|
judge: resolveJudge(opts, auth),
|
|
102
139
|
workspaceClient,
|
|
103
140
|
warehouseId,
|
|
104
|
-
|
|
141
|
+
timeoutMs,
|
|
142
|
+
retries,
|
|
143
|
+
onEvent: makeProgressReporter(runner, opts.url, machine, info)
|
|
105
144
|
});
|
|
106
145
|
} catch (err) {
|
|
107
146
|
console.error(`\nEval run failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
108
147
|
process.exitCode = 1;
|
|
109
148
|
return;
|
|
110
149
|
}
|
|
111
|
-
|
|
112
|
-
if (summary.mlflow) printMlflowOutcome(summary.mlflow);
|
|
113
|
-
else
|
|
114
|
-
if (
|
|
150
|
+
info(`\n${runner.formatSummaryLine(summary.results)}`);
|
|
151
|
+
if (summary.mlflow) printMlflowOutcome(summary.mlflow, info);
|
|
152
|
+
else info("\nMLflow evaluation run skipped — pass --experiment (or set MLFLOW_EXPERIMENT_ID) plus --profile/--databricks-host to create one.");
|
|
153
|
+
if (machine) {
|
|
154
|
+
const report = reporter === "json" ? runner.formatResultsJson(summary.results) : runner.formatResultsJUnit(summary.results);
|
|
155
|
+
if (opts.output) {
|
|
156
|
+
try {
|
|
157
|
+
fs.writeFileSync(opts.output, `${report}\n`);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.error(`Failed to write ${reporter} report to ${opts.output}: ${err instanceof Error ? err.message : String(err)}`);
|
|
160
|
+
process.exitCode = 1;
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
info(`Wrote ${reporter} report to ${opts.output}`);
|
|
164
|
+
} else process.stdout.write(`${report}\n`);
|
|
165
|
+
}
|
|
166
|
+
const stats = runner.summarize(summary.results);
|
|
167
|
+
if (minPassRate !== void 0) {
|
|
168
|
+
const ok = stats.passRate >= minPassRate;
|
|
169
|
+
info(`Pass rate ${(stats.passRate * 100).toFixed(0)}% (threshold ${(minPassRate * 100).toFixed(0)}%) — ${ok ? "OK" : "below threshold"}`);
|
|
170
|
+
if (!ok) process.exitCode = 1;
|
|
171
|
+
} else if (!stats.allPassed) process.exitCode = 1;
|
|
115
172
|
}
|
|
116
|
-
const agentEvalCommand = new Command("eval").description("Run agent evals (server/agents/<id>/evals/*.eval.ts) against a running app").argument("[filter]", "Only run evals whose <agent>/<id> contains this substring (or an exact agent id)").option("--url <url>", "Base URL of the running app", "http://localhost:3000").option("--strict", "Fail on soft-assertion misses too", false).option("--concurrency <n>", "Max evals to run concurrently (default 4; keep at or below the app's max concurrent streams per user)", (v) => Number.parseInt(v, 10)).option("--root <dir>", "Project root containing server/agents/ (default: cwd)").option("--header <header...>", "Extra request header as 'Key: value' (repeatable)").option("--profile <name>", "Databricks CLI profile to authenticate with via OAuth (default: DATABRICKS_CONFIG_PROFILE)").option("--databricks-host <host>", "Databricks host for writing MLflow assessments (default: DATABRICKS_HOST)").option("--databricks-token <token>", "Databricks token for writing MLflow assessments (default: DATABRICKS_TOKEN)").option("--experiment <id>", "MLflow experiment id for the evaluation run (default: MLFLOW_EXPERIMENT_ID)").option("--warehouse-id <id>", "SQL warehouse id for reading managed eval datasets and writing assessments to UC-backed experiments (default: DATABRICKS_WAREHOUSE_ID, or MLFLOW_TRACING_SQL_WAREHOUSE_ID for assessments)").option("--judge-model <endpoint>", "Databricks serving endpoint to use as the LLM judge for t.judge.* (default: APPKIT_JUDGE_MODEL)").
|
|
173
|
+
const agentEvalCommand = new Command("eval").description("Run agent evals (server/agents/<id>/evals/*.eval.ts) against a running app").argument("[filter]", "Only run evals whose <agent>/<id> contains this substring (or an exact agent id)").option("--url <url>", "Base URL of the running app", "http://localhost:3000").option("--strict", "Fail on soft-assertion misses too", false).option("--concurrency <n>", "Max evals to run concurrently (default 4; keep at or below the app's max concurrent streams per user)", (v) => Number.parseInt(v, 10)).option("--root <dir>", "Project root containing server/agents/ (default: cwd)").option("--header <header...>", "Extra request header as 'Key: value' (repeatable)").option("--tag <tag...>", "Only run evals tagged with one of these tags (repeatable)").option("--profile <name>", "Databricks CLI profile to authenticate with via OAuth (default: DATABRICKS_CONFIG_PROFILE)").option("--databricks-host <host>", "Databricks host for writing MLflow assessments (default: DATABRICKS_HOST)").option("--databricks-token <token>", "Databricks token for writing MLflow assessments (default: DATABRICKS_TOKEN)").option("--experiment <id>", "MLflow experiment id for the evaluation run (default: MLFLOW_EXPERIMENT_ID)").option("--warehouse-id <id>", "SQL warehouse id for reading managed eval datasets and writing assessments to UC-backed experiments (default: DATABRICKS_WAREHOUSE_ID, or MLFLOW_TRACING_SQL_WAREHOUSE_ID for assessments)").option("--judge-model <endpoint>", "Databricks serving endpoint to use as the LLM judge for t.judge.* (default: APPKIT_JUDGE_MODEL)").option("--timeout <ms>", "Default per-eval timeout in ms (a per-eval timeoutMs overrides it)").option("--retries <n>", "Re-run an eval up to N times when it fails on an infra error (turn/timeout); assertion failures are not retried").option("--min-pass-rate <rate>", "Gate on aggregate pass rate (0..1) instead of requiring every eval to pass; exit 1 when below").addOption(new Option("--reporter <format>", "Report format: text (live console), json (dashboards), or junit (CI test reporters)").choices([
|
|
174
|
+
"text",
|
|
175
|
+
"json",
|
|
176
|
+
"junit"
|
|
177
|
+
]).default("text")).option("--output <file>", "Write the json/junit report to this file instead of stdout (ignored for text)").action(runAgentEval);
|
|
117
178
|
|
|
118
179
|
//#endregion
|
|
119
180
|
export { agentEvalCommand };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eval.js","names":[],"sources":["../../../../src/cli/commands/agent/eval.ts"],"sourcesContent":["import { Command } from \"commander\";\n\ninterface EvalRunSummary {\n results: unknown[];\n mlflow?: {\n runId: string;\n report: {\n written: number;\n skipped: number;\n failures: Array<{ traceId: string; status?: number; error?: string }>;\n };\n finish: { finished: boolean; metricsError?: string; finishError?: string };\n };\n}\n\ntype EvalProgress =\n | { type: \"discovered\"; total: number }\n | { type: \"run-created\"; runId: string }\n | { type: \"start\"; id: string; index: number; total: number }\n | { type: \"result\"; result: unknown; index: number; total: number };\n\n/** Subset of `@databricks/appkit/beta`'s eval runner used by this command. */\ninterface EvalRunner {\n runEvalsInDir(opts: {\n rootDir?: string;\n baseUrl: string;\n filter?: string;\n strict?: boolean;\n headers?: Record<string, string>;\n concurrency?: number;\n mlflow?: {\n host: string;\n token: string;\n experimentId: string;\n sqlWarehouseId?: string;\n };\n judge?: { host: string; token: string; model: string };\n workspaceClient?: unknown;\n warehouseId?: string;\n onEvent?: (event: EvalProgress) => void;\n }): Promise<EvalRunSummary>;\n resolveDatabricksAuth(opts: {\n profile?: string;\n host?: string;\n token?: string;\n }): Promise<{ host: string; token: string } | undefined>;\n resolveWorkspaceClient(opts: {\n profile?: string;\n host?: string;\n token?: string;\n }): unknown;\n formatEvalHeadline(result: unknown): string;\n evalGlyph(result: unknown): string;\n formatEvalDetail(result: unknown): string[];\n formatSummaryLine(results: unknown[]): string;\n summarize(results: unknown[]): { allPassed: boolean };\n}\n\n/**\n * Loaded at runtime from the consuming project so this command (which ships in\n * `@databricks/shared`) doesn't take a build-time dependency on appkit. The\n * specifier is a variable so the type checker treats it as `any`.\n */\nasync function loadRunner(): Promise<EvalRunner> {\n const spec = \"@databricks/appkit/beta\";\n try {\n return (await import(spec)) as unknown as EvalRunner;\n } catch (err) {\n throw new Error(\n \"Could not load @databricks/appkit. Run `appkit agent eval` from a \" +\n \"project with @databricks/appkit installed. \" +\n `Cause: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nfunction parseHeaders(values: string[]): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const v of values) {\n const i = v.indexOf(\":\");\n if (i === -1) continue;\n headers[v.slice(0, i).trim()] = v.slice(i + 1).trim();\n }\n return headers;\n}\n\ninterface EvalOptions {\n url: string;\n strict?: boolean;\n root?: string;\n header?: string[];\n profile?: string;\n databricksHost?: string;\n databricksToken?: string;\n experiment?: string;\n judgeModel?: string;\n concurrency?: number;\n warehouseId?: string;\n}\n\n/** Resolved Databricks host + bearer (either field may be absent). */\ntype Auth = { host?: string; token?: string };\n\n/**\n * Native MLflow \"Evaluation run\" config — only when creds + an experiment are\n * all present (traces live in the app; the run + scores are driven from here).\n */\nfunction resolveMlflow(opts: EvalOptions, auth: Auth) {\n const experimentId = opts.experiment ?? process.env.MLFLOW_EXPERIMENT_ID;\n if (!(auth.host && auth.token && experimentId)) return undefined;\n // UC-backed experiments need a SQL warehouse to write assessments to their\n // V4 traces. Mirror mlflow's env var, and accept the common DATABRICKS one.\n const sqlWarehouseId =\n opts.warehouseId ??\n process.env.MLFLOW_TRACING_SQL_WAREHOUSE_ID ??\n process.env.DATABRICKS_WAREHOUSE_ID;\n return {\n host: auth.host,\n token: auth.token,\n experimentId,\n ...(sqlWarehouseId ? { sqlWarehouseId } : {}),\n };\n}\n\n/** LLM-as-judge config — reuses the Databricks creds + a judge serving endpoint. */\nfunction resolveJudge(opts: EvalOptions, auth: Auth) {\n const model = opts.judgeModel ?? process.env.APPKIT_JUDGE_MODEL;\n return model && auth.host && auth.token\n ? { host: auth.host, token: auth.token, model }\n : undefined;\n}\n\n/** Progress reporter: stream each eval as it runs instead of going silent. */\nfunction makeProgressReporter(\n runner: EvalRunner,\n url: string,\n): (event: EvalProgress) => void {\n return (event) => {\n switch (event.type) {\n case \"discovered\":\n console.log(\n `Running ${event.total} eval${event.total === 1 ? \"\" : \"s\"} against ${url}\\n`,\n );\n break;\n case \"run-created\":\n console.log(`MLflow evaluation run: ${event.runId}\\n`);\n break;\n case \"result\": {\n // One full line per completion — evals run concurrently, so a split\n // \"start … glyph\" prefix would interleave into garbage.\n console.log(\n `[${event.index + 1}/${event.total}] ${runner.formatEvalHeadline(event.result)}`,\n );\n for (const line of runner.formatEvalDetail(event.result)) {\n console.log(line);\n }\n break;\n }\n }\n };\n}\n\nfunction formatFailureLine(f: {\n traceId: string;\n status?: number;\n error?: string;\n}): string {\n return ` ✗ trace ${f.traceId}: ${f.status ?? \"\"} ${f.error ?? \"\"}`.trim();\n}\n\n/** Print the MLflow assessment/finish outcome after a run that created one. */\nfunction printMlflowOutcome(\n mlflow: NonNullable<EvalRunSummary[\"mlflow\"]>,\n): void {\n const { report, finish } = mlflow;\n console.log(\n `MLflow: ${report.written} assessment(s) written` +\n (report.skipped ? `, ${report.skipped} skipped` : \"\") +\n (report.failures.length ? `, ${report.failures.length} failed` : \"\"),\n );\n for (const f of report.failures) {\n console.error(formatFailureLine(f));\n }\n if (finish.metricsError) {\n console.error(` ⚠ metrics not logged: ${finish.metricsError}`);\n }\n if (!finish.finished) {\n console.error(\n ` ✗ run left RUNNING — failed to finish: ${finish.finishError ?? \"unknown\"}`,\n );\n }\n}\n\nasync function runAgentEval(\n filter: string | undefined,\n opts: EvalOptions,\n): Promise<void> {\n const runner = await loadRunner();\n\n // Resolve Databricks host + bearer the AppKit-native way: an explicit\n // host/token (or DATABRICKS_* env) wins; otherwise the SDK mints an OAuth\n // token from the CLI profile — so no hand-set PAT is required.\n const auth: Auth =\n (await runner.resolveDatabricksAuth({\n profile: opts.profile ?? process.env.DATABRICKS_CONFIG_PROFILE,\n host: opts.databricksHost ?? process.env.DATABRICKS_HOST,\n token: opts.databricksToken ?? process.env.DATABRICKS_TOKEN,\n })) ?? {};\n\n // Managed-dataset reads: a workspace client (same profile/host/token) + a SQL\n // warehouse. Only needed by evals that declare `dataset`.\n const warehouseId = opts.warehouseId ?? process.env.DATABRICKS_WAREHOUSE_ID;\n const workspaceClient = runner.resolveWorkspaceClient({\n profile: opts.profile ?? process.env.DATABRICKS_CONFIG_PROFILE,\n host: opts.databricksHost ?? process.env.DATABRICKS_HOST,\n token: opts.databricksToken ?? process.env.DATABRICKS_TOKEN,\n });\n\n let summary: EvalRunSummary;\n try {\n summary = await runner.runEvalsInDir({\n rootDir: opts.root,\n baseUrl: opts.url,\n filter,\n strict: opts.strict,\n headers: opts.header ? parseHeaders(opts.header) : undefined,\n concurrency: opts.concurrency,\n mlflow: resolveMlflow(opts, auth),\n judge: resolveJudge(opts, auth),\n workspaceClient,\n warehouseId,\n onEvent: makeProgressReporter(runner, opts.url),\n });\n } catch (err) {\n // Setup failures (e.g. a bad --experiment for the MLflow run) reject before\n // any eval runs; surface a clean message + non-zero exit rather than an\n // unhandled promise rejection with a raw stack.\n console.error(\n `\\nEval run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exitCode = 1;\n return;\n }\n console.log(`\\n${runner.formatSummaryLine(summary.results)}`);\n\n if (summary.mlflow) {\n printMlflowOutcome(summary.mlflow);\n } else {\n console.log(\n \"\\nMLflow evaluation run skipped — pass --experiment (or set\" +\n \" MLFLOW_EXPERIMENT_ID) plus --profile/--databricks-host to create one.\",\n );\n }\n\n if (!runner.summarize(summary.results).allPassed) {\n process.exitCode = 1;\n }\n}\n\nexport const agentEvalCommand = new Command(\"eval\")\n .description(\n \"Run agent evals (server/agents/<id>/evals/*.eval.ts) against a running app\",\n )\n .argument(\n \"[filter]\",\n \"Only run evals whose <agent>/<id> contains this substring (or an exact agent id)\",\n )\n .option(\"--url <url>\", \"Base URL of the running app\", \"http://localhost:3000\")\n .option(\"--strict\", \"Fail on soft-assertion misses too\", false)\n .option(\n \"--concurrency <n>\",\n \"Max evals to run concurrently (default 4; keep at or below the app's max concurrent streams per user)\",\n (v) => Number.parseInt(v, 10),\n )\n .option(\n \"--root <dir>\",\n \"Project root containing server/agents/ (default: cwd)\",\n )\n .option(\n \"--header <header...>\",\n \"Extra request header as 'Key: value' (repeatable)\",\n )\n .option(\n \"--profile <name>\",\n \"Databricks CLI profile to authenticate with via OAuth (default: DATABRICKS_CONFIG_PROFILE)\",\n )\n .option(\n \"--databricks-host <host>\",\n \"Databricks host for writing MLflow assessments (default: DATABRICKS_HOST)\",\n )\n .option(\n \"--databricks-token <token>\",\n \"Databricks token for writing MLflow assessments (default: DATABRICKS_TOKEN)\",\n )\n .option(\n \"--experiment <id>\",\n \"MLflow experiment id for the evaluation run (default: MLFLOW_EXPERIMENT_ID)\",\n )\n .option(\n \"--warehouse-id <id>\",\n \"SQL warehouse id for reading managed eval datasets and writing assessments to UC-backed experiments (default: DATABRICKS_WAREHOUSE_ID, or MLFLOW_TRACING_SQL_WAREHOUSE_ID for assessments)\",\n )\n .option(\n \"--judge-model <endpoint>\",\n \"Databricks serving endpoint to use as the LLM judge for t.judge.* (default: APPKIT_JUDGE_MODEL)\",\n )\n .action(runAgentEval);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"eval.js","names":[],"sources":["../../../../src/cli/commands/agent/eval.ts"],"sourcesContent":["import fs from \"node:fs\";\n\nimport { Command, Option } from \"commander\";\n\ninterface EvalRunSummary {\n results: unknown[];\n mlflow?: {\n runId: string;\n report: {\n written: number;\n skipped: number;\n failures: Array<{ traceId: string; status?: number; error?: string }>;\n };\n finish: { finished: boolean; metricsError?: string; finishError?: string };\n };\n}\n\ntype EvalProgress =\n | { type: \"discovered\"; total: number }\n | { type: \"run-created\"; runId: string }\n | { type: \"start\"; id: string; index: number; total: number }\n | { type: \"result\"; result: unknown; index: number; total: number };\n\n/** Subset of `@databricks/appkit/beta`'s eval runner used by this command. */\ninterface EvalRunner {\n runEvalsInDir(opts: {\n rootDir?: string;\n baseUrl: string;\n filter?: string;\n tags?: string[];\n strict?: boolean;\n headers?: Record<string, string>;\n concurrency?: number;\n mlflow?: {\n host: string;\n token: string;\n experimentId: string;\n sqlWarehouseId?: string;\n };\n judge?: { host: string; token: string; model: string };\n workspaceClient?: unknown;\n warehouseId?: string;\n timeoutMs?: number;\n retries?: number;\n onEvent?: (event: EvalProgress) => void;\n }): Promise<EvalRunSummary>;\n resolveDatabricksAuth(opts: {\n profile?: string;\n host?: string;\n token?: string;\n }): Promise<{ host: string; token: string } | undefined>;\n resolveWorkspaceClient(opts: {\n profile?: string;\n host?: string;\n token?: string;\n }): unknown;\n formatEvalHeadline(result: unknown): string;\n evalGlyph(result: unknown): string;\n formatEvalDetail(result: unknown): string[];\n formatSummaryLine(results: unknown[]): string;\n formatResultsJson(results: unknown[]): string;\n formatResultsJUnit(results: unknown[]): string;\n summarize(results: unknown[]): { allPassed: boolean; passRate: number };\n}\n\n/**\n * Loaded at runtime from the consuming project so this command (which ships in\n * `@databricks/shared`) doesn't take a build-time dependency on appkit. The\n * specifier is a variable so the type checker treats it as `any`.\n */\nasync function loadRunner(): Promise<EvalRunner> {\n const spec = \"@databricks/appkit/beta\";\n try {\n return (await import(spec)) as unknown as EvalRunner;\n } catch (err) {\n throw new Error(\n \"Could not load @databricks/appkit. Run `appkit agent eval` from a \" +\n \"project with @databricks/appkit installed. \" +\n `Cause: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nfunction parseHeaders(values: string[]): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const v of values) {\n const i = v.indexOf(\":\");\n if (i === -1) continue;\n headers[v.slice(0, i).trim()] = v.slice(i + 1).trim();\n }\n return headers;\n}\n\n/**\n * Parse `--min-pass-rate`: a finite number in `[0, 1]`, or `undefined` when\n * unset. Throws on blank, out-of-range, or non-numeric input so a bad gate\n * value fails fast instead of silently disabling or inverting the CI gate.\n */\nexport function parsePassRate(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n // Reject blank too: `Number(\"\")` is 0, which would silently disable the gate.\n if (raw.trim() === \"\" || !Number.isFinite(n) || n < 0 || n > 1) {\n throw new Error(\n `Invalid --min-pass-rate \"${raw}\" — expected a number in [0, 1]`,\n );\n }\n return n;\n}\n\ninterface EvalOptions {\n url: string;\n strict?: boolean;\n root?: string;\n header?: string[];\n tag?: string[];\n profile?: string;\n databricksHost?: string;\n databricksToken?: string;\n experiment?: string;\n judgeModel?: string;\n concurrency?: number;\n warehouseId?: string;\n timeout?: string;\n retries?: string;\n minPassRate?: string;\n reporter?: \"text\" | \"json\" | \"junit\";\n output?: string;\n}\n\n/** Resolved Databricks host + bearer (either field may be absent). */\ntype Auth = { host?: string; token?: string };\n\n/**\n * Native MLflow \"Evaluation run\" config — only when creds + an experiment are\n * all present (traces live in the app; the run + scores are driven from here).\n */\nfunction resolveMlflow(opts: EvalOptions, auth: Auth) {\n const experimentId = opts.experiment ?? process.env.MLFLOW_EXPERIMENT_ID;\n if (!(auth.host && auth.token && experimentId)) return undefined;\n // UC-backed experiments need a SQL warehouse to write assessments to their\n // V4 traces. Mirror mlflow's env var, and accept the common DATABRICKS one.\n const sqlWarehouseId =\n opts.warehouseId ??\n process.env.MLFLOW_TRACING_SQL_WAREHOUSE_ID ??\n process.env.DATABRICKS_WAREHOUSE_ID;\n return {\n host: auth.host,\n token: auth.token,\n experimentId,\n ...(sqlWarehouseId ? { sqlWarehouseId } : {}),\n };\n}\n\n/** LLM-as-judge config — reuses the Databricks creds + a judge serving endpoint. */\nfunction resolveJudge(opts: EvalOptions, auth: Auth) {\n const model = opts.judgeModel ?? process.env.APPKIT_JUDGE_MODEL;\n return model && auth.host && auth.token\n ? { host: auth.host, token: auth.token, model }\n : undefined;\n}\n\n/**\n * Progress reporter: stream each eval as it runs instead of going silent. In a\n * machine reporter (json/junit) the live per-eval streaming is suppressed and\n * banners go to stderr (via `info`), keeping stdout clean for the report.\n */\nfunction makeProgressReporter(\n runner: EvalRunner,\n url: string,\n machine: boolean,\n info: (msg: string) => void,\n): (event: EvalProgress) => void {\n return (event) => {\n switch (event.type) {\n case \"discovered\":\n info(\n `Running ${event.total} eval${event.total === 1 ? \"\" : \"s\"} against ${url}\\n`,\n );\n break;\n case \"run-created\":\n info(`MLflow evaluation run: ${event.runId}\\n`);\n break;\n case \"result\": {\n if (machine) break;\n // One full line per completion — evals run concurrently, so a split\n // \"start … glyph\" prefix would interleave into garbage.\n console.log(\n `[${event.index + 1}/${event.total}] ${runner.formatEvalHeadline(event.result)}`,\n );\n for (const line of runner.formatEvalDetail(event.result)) {\n console.log(line);\n }\n break;\n }\n }\n };\n}\n\nfunction formatFailureLine(f: {\n traceId: string;\n status?: number;\n error?: string;\n}): string {\n return ` ✗ trace ${f.traceId}: ${f.status ?? \"\"} ${f.error ?? \"\"}`.trim();\n}\n\n/**\n * Print the MLflow assessment/finish outcome after a run that created one. The\n * summary line goes through `info` (stderr under a machine reporter); per-trace\n * failures and finish errors always go to stderr.\n */\nfunction printMlflowOutcome(\n mlflow: NonNullable<EvalRunSummary[\"mlflow\"]>,\n info: (msg: string) => void,\n): void {\n const { report, finish } = mlflow;\n info(\n `MLflow: ${report.written} assessment(s) written` +\n (report.skipped ? `, ${report.skipped} skipped` : \"\") +\n (report.failures.length ? `, ${report.failures.length} failed` : \"\"),\n );\n for (const f of report.failures) {\n console.error(formatFailureLine(f));\n }\n if (finish.metricsError) {\n console.error(` ⚠ metrics not logged: ${finish.metricsError}`);\n }\n if (!finish.finished) {\n console.error(\n ` ✗ run left RUNNING — failed to finish: ${finish.finishError ?? \"unknown\"}`,\n );\n }\n}\n\nasync function runAgentEval(\n filter: string | undefined,\n opts: EvalOptions,\n): Promise<void> {\n const runner = await loadRunner();\n\n // Databricks credentials shared by auth resolution and the workspace client:\n // an explicit flag/DATABRICKS_* env wins, else the SDK resolves from the CLI\n // profile.\n const credentials = {\n profile: opts.profile ?? process.env.DATABRICKS_CONFIG_PROFILE,\n host: opts.databricksHost ?? process.env.DATABRICKS_HOST,\n token: opts.databricksToken ?? process.env.DATABRICKS_TOKEN,\n };\n\n // Resolve Databricks host + bearer the AppKit-native way: an explicit\n // host/token wins; otherwise the SDK mints an OAuth token from the CLI\n // profile — so no hand-set PAT is required.\n const auth: Auth = (await runner.resolveDatabricksAuth(credentials)) ?? {};\n\n // Managed-dataset reads: a workspace client (same profile/host/token) + a SQL\n // warehouse. Only needed by evals that declare `dataset`.\n const warehouseId = opts.warehouseId ?? process.env.DATABRICKS_WAREHOUSE_ID;\n const workspaceClient = runner.resolveWorkspaceClient(credentials);\n\n // Runner-level default per-eval timeout (ms). A per-eval `timeoutMs` wins.\n const parsedTimeout = opts.timeout\n ? Number.parseInt(opts.timeout, 10)\n : undefined;\n const timeoutMs =\n parsedTimeout && parsedTimeout > 0 ? parsedTimeout : undefined;\n\n // Extra attempts for evals that fail on an infra error (turn/timeout). Junk\n // or negative input falls back to no retries.\n const parsedRetries = opts.retries\n ? Number.parseInt(opts.retries, 10)\n : undefined;\n const retries =\n parsedRetries && parsedRetries > 0 ? parsedRetries : undefined;\n\n // Validate up front so a bad gate value fails before the run, not after.\n let minPassRate: number | undefined;\n try {\n minPassRate = parsePassRate(opts.minPassRate);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n return;\n }\n\n // In a machine reporter (json/junit), stdout is reserved for the report (it\n // may be piped), so human-facing lines go to stderr and the per-eval live\n // streaming is suppressed. Text mode keeps its current stdout behavior.\n const reporter = opts.reporter ?? \"text\";\n const machine = reporter !== \"text\";\n const info = (msg: string): void => {\n if (machine) console.error(msg);\n else console.log(msg);\n };\n\n let summary: EvalRunSummary;\n try {\n summary = await runner.runEvalsInDir({\n rootDir: opts.root,\n baseUrl: opts.url,\n filter,\n tags: opts.tag,\n strict: opts.strict,\n headers: opts.header ? parseHeaders(opts.header) : undefined,\n concurrency: opts.concurrency,\n mlflow: resolveMlflow(opts, auth),\n judge: resolveJudge(opts, auth),\n workspaceClient,\n warehouseId,\n timeoutMs,\n retries,\n onEvent: makeProgressReporter(runner, opts.url, machine, info),\n });\n } catch (err) {\n // Setup failures (e.g. a bad --experiment for the MLflow run) reject before\n // any eval runs; surface a clean message + non-zero exit rather than an\n // unhandled promise rejection with a raw stack.\n console.error(\n `\\nEval run failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exitCode = 1;\n return;\n }\n\n // The final human summary always shows (stderr for machine reporters so it\n // never pollutes the report on stdout/file).\n info(`\\n${runner.formatSummaryLine(summary.results)}`);\n\n if (summary.mlflow) {\n printMlflowOutcome(summary.mlflow, info);\n } else {\n info(\n \"\\nMLflow evaluation run skipped — pass --experiment (or set\" +\n \" MLFLOW_EXPERIMENT_ID) plus --profile/--databricks-host to create one.\",\n );\n }\n\n // Machine-readable report: build the string with a pure formatter, then emit\n // it to --output <file> or stdout (kept clean of the human noise above).\n if (machine) {\n const report =\n reporter === \"json\"\n ? runner.formatResultsJson(summary.results)\n : runner.formatResultsJUnit(summary.results);\n if (opts.output) {\n try {\n fs.writeFileSync(opts.output, `${report}\\n`);\n } catch (err) {\n console.error(\n `Failed to write ${reporter} report to ${opts.output}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n process.exitCode = 1;\n return;\n }\n info(`Wrote ${reporter} report to ${opts.output}`);\n } else {\n process.stdout.write(`${report}\\n`);\n }\n }\n\n const stats = runner.summarize(summary.results);\n if (minPassRate !== undefined) {\n // Threshold mode: gate on the aggregate pass rate rather than requiring\n // every eval to pass.\n const ok = stats.passRate >= minPassRate;\n info(\n `Pass rate ${(stats.passRate * 100).toFixed(0)}% (threshold ${(\n minPassRate * 100\n ).toFixed(0)}%) — ${ok ? \"OK\" : \"below threshold\"}`,\n );\n if (!ok) process.exitCode = 1;\n } else if (!stats.allPassed) {\n process.exitCode = 1;\n }\n}\n\nexport const agentEvalCommand = new Command(\"eval\")\n .description(\n \"Run agent evals (server/agents/<id>/evals/*.eval.ts) against a running app\",\n )\n .argument(\n \"[filter]\",\n \"Only run evals whose <agent>/<id> contains this substring (or an exact agent id)\",\n )\n .option(\"--url <url>\", \"Base URL of the running app\", \"http://localhost:3000\")\n .option(\"--strict\", \"Fail on soft-assertion misses too\", false)\n .option(\n \"--concurrency <n>\",\n \"Max evals to run concurrently (default 4; keep at or below the app's max concurrent streams per user)\",\n (v) => Number.parseInt(v, 10),\n )\n .option(\n \"--root <dir>\",\n \"Project root containing server/agents/ (default: cwd)\",\n )\n .option(\n \"--header <header...>\",\n \"Extra request header as 'Key: value' (repeatable)\",\n )\n .option(\n \"--tag <tag...>\",\n \"Only run evals tagged with one of these tags (repeatable)\",\n )\n .option(\n \"--profile <name>\",\n \"Databricks CLI profile to authenticate with via OAuth (default: DATABRICKS_CONFIG_PROFILE)\",\n )\n .option(\n \"--databricks-host <host>\",\n \"Databricks host for writing MLflow assessments (default: DATABRICKS_HOST)\",\n )\n .option(\n \"--databricks-token <token>\",\n \"Databricks token for writing MLflow assessments (default: DATABRICKS_TOKEN)\",\n )\n .option(\n \"--experiment <id>\",\n \"MLflow experiment id for the evaluation run (default: MLFLOW_EXPERIMENT_ID)\",\n )\n .option(\n \"--warehouse-id <id>\",\n \"SQL warehouse id for reading managed eval datasets and writing assessments to UC-backed experiments (default: DATABRICKS_WAREHOUSE_ID, or MLFLOW_TRACING_SQL_WAREHOUSE_ID for assessments)\",\n )\n .option(\n \"--judge-model <endpoint>\",\n \"Databricks serving endpoint to use as the LLM judge for t.judge.* (default: APPKIT_JUDGE_MODEL)\",\n )\n .option(\n \"--timeout <ms>\",\n \"Default per-eval timeout in ms (a per-eval timeoutMs overrides it)\",\n )\n .option(\n \"--retries <n>\",\n \"Re-run an eval up to N times when it fails on an infra error (turn/timeout); assertion failures are not retried\",\n )\n .option(\n \"--min-pass-rate <rate>\",\n \"Gate on aggregate pass rate (0..1) instead of requiring every eval to pass; exit 1 when below\",\n )\n .addOption(\n new Option(\n \"--reporter <format>\",\n \"Report format: text (live console), json (dashboards), or junit (CI test reporters)\",\n )\n .choices([\"text\", \"json\", \"junit\"])\n .default(\"text\"),\n )\n .option(\n \"--output <file>\",\n \"Write the json/junit report to this file instead of stdout (ignored for text)\",\n )\n .action(runAgentEval);\n"],"mappings":"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"}
|
package/dist/evals/dataset.d.ts
CHANGED
|
@@ -19,6 +19,19 @@ interface ReadEvalDatasetOptions {
|
|
|
19
19
|
/** Optional row cap. */
|
|
20
20
|
limit?: number;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Extract every user-message content, in order, from an MLflow
|
|
24
|
+
* `{"messages":[{"role":"user","content":"..."}]}` input. A dataset row can
|
|
25
|
+
* carry a full multi-turn conversation; replaying these against one thread (one
|
|
26
|
+
* `t.send` per returned string) lets the agent see the accumulating history.
|
|
27
|
+
*
|
|
28
|
+
* Only `role === "user"` turns are returned — any interleaved `assistant`/
|
|
29
|
+
* `system` messages in the row are ignored, since the agent generates its own
|
|
30
|
+
* responses; you never inject the dataset's assistant turns. A single-user-turn
|
|
31
|
+
* row yields a one-element array (backward compatible); a row with no `messages`
|
|
32
|
+
* yields `[]`.
|
|
33
|
+
*/
|
|
34
|
+
declare function userTurns(input: Record<string, unknown>): string[];
|
|
22
35
|
/**
|
|
23
36
|
* Read a Databricks managed evaluation dataset (a Unity Catalog table with
|
|
24
37
|
* `inputs`/`expectations` columns) into rows, over the public SQL Statement
|
|
@@ -32,5 +45,5 @@ interface ReadEvalDatasetOptions {
|
|
|
32
45
|
*/
|
|
33
46
|
declare function readEvalDataset(client: WorkspaceClient, options: ReadEvalDatasetOptions): Promise<DatasetRow[]>;
|
|
34
47
|
//#endregion
|
|
35
|
-
export { DatasetRow, ReadEvalDatasetOptions, readEvalDataset };
|
|
48
|
+
export { DatasetRow, ReadEvalDatasetOptions, readEvalDataset, userTurns };
|
|
36
49
|
//# sourceMappingURL=dataset.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset.d.ts","names":[],"sources":["../../src/evals/dataset.ts"],"mappings":";;;;;AASA;;;;UAAiB,UAAA;EACf,MAAA,EAAQ,MAAA;EACR,YAAA,GAAe,MAAA;AAAA;AAAA,UAGA,sBAAA;EAHM;EAKrB,KAAA;EAFqC;EAIrC,WAAA;EAJqC;EAMrC,KAAA;AAAA;;;;
|
|
1
|
+
{"version":3,"file":"dataset.d.ts","names":[],"sources":["../../src/evals/dataset.ts"],"mappings":";;;;;AASA;;;;UAAiB,UAAA;EACf,MAAA,EAAQ,MAAA;EACR,YAAA,GAAe,MAAA;AAAA;AAAA,UAGA,sBAAA;EAHM;EAKrB,KAAA;EAFqC;EAIrC,WAAA;EAJqC;EAMrC,KAAA;AAAA;;;;AAeF;;;;;AAkCA;;;;iBAlCgB,SAAA,CAAU,KAAA,EAAO,MAAA;;;;;;;;;;;;iBAkCX,eAAA,CACpB,MAAA,EAAQ,eAAA,EACR,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,UAAA"}
|
package/dist/evals/dataset.js
CHANGED
|
@@ -2,6 +2,21 @@ import { SQLWarehouseConnector } from "../connectors/sql-warehouse/client.js";
|
|
|
2
2
|
import "../connectors/index.js";
|
|
3
3
|
|
|
4
4
|
//#region src/evals/dataset.ts
|
|
5
|
+
/**
|
|
6
|
+
* Extract every user-message content, in order, from an MLflow
|
|
7
|
+
* `{"messages":[{"role":"user","content":"..."}]}` input. A dataset row can
|
|
8
|
+
* carry a full multi-turn conversation; replaying these against one thread (one
|
|
9
|
+
* `t.send` per returned string) lets the agent see the accumulating history.
|
|
10
|
+
*
|
|
11
|
+
* Only `role === "user"` turns are returned — any interleaved `assistant`/
|
|
12
|
+
* `system` messages in the row are ignored, since the agent generates its own
|
|
13
|
+
* responses; you never inject the dataset's assistant turns. A single-user-turn
|
|
14
|
+
* row yields a one-element array (backward compatible); a row with no `messages`
|
|
15
|
+
* yields `[]`.
|
|
16
|
+
*/
|
|
17
|
+
function userTurns(input) {
|
|
18
|
+
return (Array.isArray(input.messages) ? input.messages : []).filter((m) => typeof m === "object" && m !== null).filter((m) => m.role === "user").map((m) => typeof m.content === "string" ? m.content : "");
|
|
19
|
+
}
|
|
5
20
|
/** A managed eval dataset is a UC table; only 3-level names are valid. */
|
|
6
21
|
const UC_TABLE = /^[A-Za-z0-9_]+\.[A-Za-z0-9_]+\.[A-Za-z0-9_]+$/;
|
|
7
22
|
/** Coerce a cell (already JSON-parsed by the connector for JSON columns) to a record. */
|
|
@@ -32,5 +47,5 @@ async function readEvalDataset(client, options) {
|
|
|
32
47
|
}
|
|
33
48
|
|
|
34
49
|
//#endregion
|
|
35
|
-
export { readEvalDataset };
|
|
50
|
+
export { readEvalDataset, userTurns };
|
|
36
51
|
//# sourceMappingURL=dataset.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset.js","names":[],"sources":["../../src/evals/dataset.ts"],"sourcesContent":["import { SQLWarehouseConnector } from \"../connectors\";\nimport type { WorkspaceClient } from \"../workspace-client\";\n\n/**\n * One row of a managed evaluation dataset. `inputs` are the kwargs passed to the\n * agent for the turn; `expectations` (when present) is the row's ground truth /\n * guidelines. Mirrors the `{inputs, expectations}` shape of `mlflow.genai`\n * datasets and of the Unity Catalog table backing a managed eval dataset.\n */\nexport interface DatasetRow {\n inputs: Record<string, unknown>;\n expectations?: Record<string, unknown>;\n}\n\nexport interface ReadEvalDatasetOptions {\n /** Fully-qualified UC table: `catalog.schema.table`. */\n table: string;\n /** SQL warehouse id to run the read against. */\n warehouseId: string;\n /** Optional row cap. */\n limit?: number;\n}\n\n/** A managed eval dataset is a UC table; only 3-level names are valid. */\nconst UC_TABLE = /^[A-Za-z0-9_]+\\.[A-Za-z0-9_]+\\.[A-Za-z0-9_]+$/;\n\n/** Coerce a cell (already JSON-parsed by the connector for JSON columns) to a record. */\nfunction toRecord(value: unknown): Record<string, unknown> | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\n/**\n * Read a Databricks managed evaluation dataset (a Unity Catalog table with\n * `inputs`/`expectations` columns) into rows, over the public SQL Statement\n * Execution API. Reuses {@link SQLWarehouseConnector} for submit/poll/transform\n * — its result transform already JSON-parses string columns into objects, so\n * `inputs`/`expectations` come back as records whether the table stores them as\n * JSON strings or structs.\n *\n * The Python `mlflow.genai.datasets` API needs a Spark session (no TS\n * equivalent), so we read the backing table directly.\n */\nexport async function readEvalDataset(\n client: WorkspaceClient,\n options: ReadEvalDatasetOptions,\n): Promise<DatasetRow[]> {\n if (!UC_TABLE.test(options.table)) {\n throw new Error(\n `Invalid dataset table \"${options.table}\" — expected catalog.schema.table`,\n );\n }\n\n const limit =\n typeof options.limit === \"number\" && options.limit > 0\n ? ` LIMIT ${Math.floor(options.limit)}`\n : \"\";\n const connector = new SQLWarehouseConnector({});\n const response = await connector.executeStatement(client, {\n warehouse_id: options.warehouseId,\n statement: `SELECT inputs, expectations FROM ${options.table}${limit}`,\n });\n\n const rows =\n (response.result as { data?: Array<Record<string, unknown>> } | undefined)\n ?.data ?? [];\n\n return rows.map((row) => ({\n inputs: toRecord(row.inputs) ?? {},\n expectations: toRecord(row.expectations),\n }));\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dataset.js","names":[],"sources":["../../src/evals/dataset.ts"],"sourcesContent":["import { SQLWarehouseConnector } from \"../connectors\";\nimport type { WorkspaceClient } from \"../workspace-client\";\n\n/**\n * One row of a managed evaluation dataset. `inputs` are the kwargs passed to the\n * agent for the turn; `expectations` (when present) is the row's ground truth /\n * guidelines. Mirrors the `{inputs, expectations}` shape of `mlflow.genai`\n * datasets and of the Unity Catalog table backing a managed eval dataset.\n */\nexport interface DatasetRow {\n inputs: Record<string, unknown>;\n expectations?: Record<string, unknown>;\n}\n\nexport interface ReadEvalDatasetOptions {\n /** Fully-qualified UC table: `catalog.schema.table`. */\n table: string;\n /** SQL warehouse id to run the read against. */\n warehouseId: string;\n /** Optional row cap. */\n limit?: number;\n}\n\n/**\n * Extract every user-message content, in order, from an MLflow\n * `{\"messages\":[{\"role\":\"user\",\"content\":\"...\"}]}` input. A dataset row can\n * carry a full multi-turn conversation; replaying these against one thread (one\n * `t.send` per returned string) lets the agent see the accumulating history.\n *\n * Only `role === \"user\"` turns are returned — any interleaved `assistant`/\n * `system` messages in the row are ignored, since the agent generates its own\n * responses; you never inject the dataset's assistant turns. A single-user-turn\n * row yields a one-element array (backward compatible); a row with no `messages`\n * yields `[]`.\n */\nexport function userTurns(input: Record<string, unknown>): string[] {\n const messages = Array.isArray(input.messages) ? input.messages : [];\n // Dataset rows are external data: skip null/non-object entries; coerce non-string content to \"\".\n return messages\n .filter(\n (m): m is { role?: unknown; content?: unknown } =>\n typeof m === \"object\" && m !== null,\n )\n .filter((m) => m.role === \"user\")\n .map((m) => (typeof m.content === \"string\" ? m.content : \"\"));\n}\n\n/** A managed eval dataset is a UC table; only 3-level names are valid. */\nconst UC_TABLE = /^[A-Za-z0-9_]+\\.[A-Za-z0-9_]+\\.[A-Za-z0-9_]+$/;\n\n/** Coerce a cell (already JSON-parsed by the connector for JSON columns) to a record. */\nfunction toRecord(value: unknown): Record<string, unknown> | undefined {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\n/**\n * Read a Databricks managed evaluation dataset (a Unity Catalog table with\n * `inputs`/`expectations` columns) into rows, over the public SQL Statement\n * Execution API. Reuses {@link SQLWarehouseConnector} for submit/poll/transform\n * — its result transform already JSON-parses string columns into objects, so\n * `inputs`/`expectations` come back as records whether the table stores them as\n * JSON strings or structs.\n *\n * The Python `mlflow.genai.datasets` API needs a Spark session (no TS\n * equivalent), so we read the backing table directly.\n */\nexport async function readEvalDataset(\n client: WorkspaceClient,\n options: ReadEvalDatasetOptions,\n): Promise<DatasetRow[]> {\n if (!UC_TABLE.test(options.table)) {\n throw new Error(\n `Invalid dataset table \"${options.table}\" — expected catalog.schema.table`,\n );\n }\n\n const limit =\n typeof options.limit === \"number\" && options.limit > 0\n ? ` LIMIT ${Math.floor(options.limit)}`\n : \"\";\n const connector = new SQLWarehouseConnector({});\n const response = await connector.executeStatement(client, {\n warehouse_id: options.warehouseId,\n statement: `SELECT inputs, expectations FROM ${options.table}${limit}`,\n });\n\n const rows =\n (response.result as { data?: Array<Record<string, unknown>> } | undefined)\n ?.data ?? [];\n\n return rows.map((row) => ({\n inputs: toRecord(row.inputs) ?? {},\n expectations: toRecord(row.expectations),\n }));\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmCA,SAAgB,UAAU,OAA0C;AAGlE,SAFiB,MAAM,QAAQ,MAAM,SAAS,GAAG,MAAM,WAAW,EAAE,EAGjE,QACE,MACC,OAAO,MAAM,YAAY,MAAM,KAClC,CACA,QAAQ,MAAM,EAAE,SAAS,OAAO,CAChC,KAAK,MAAO,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,GAAI;;;AAIjE,MAAM,WAAW;;AAGjB,SAAS,SAAS,OAAqD;AACrE,KAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CAC7D,QAAO;;;;;;;;;;;;;AAgBX,eAAsB,gBACpB,QACA,SACuB;AACvB,KAAI,CAAC,SAAS,KAAK,QAAQ,MAAM,CAC/B,OAAM,IAAI,MACR,0BAA0B,QAAQ,MAAM,mCACzC;CAGH,MAAM,QACJ,OAAO,QAAQ,UAAU,YAAY,QAAQ,QAAQ,IACjD,UAAU,KAAK,MAAM,QAAQ,MAAM,KACnC;AAWN,UATiB,MADC,IAAI,sBAAsB,EAAE,CAAC,CACd,iBAAiB,QAAQ;EACxD,cAAc,QAAQ;EACtB,WAAW,oCAAoC,QAAQ,QAAQ;EAChE,CAAC,EAGU,QACN,QAAQ,EAAE,EAEJ,KAAK,SAAS;EACxB,QAAQ,SAAS,IAAI,OAAO,IAAI,EAAE;EAClC,cAAc,SAAS,IAAI,aAAa;EACzC,EAAE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EvalDefinition } from "./types.js";
|
|
1
|
+
import { EvalConfig, EvalDefinition } from "./types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/evals/define-eval.d.ts
|
|
4
4
|
/**
|
|
@@ -21,6 +21,8 @@ import { EvalDefinition } from "./types.js";
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
declare function defineEval(def: EvalDefinition): EvalDefinition;
|
|
24
|
+
/** Define per-directory eval config. Default-export from `evals.config.ts`. */
|
|
25
|
+
declare function defineEvalConfig(config: EvalConfig): EvalConfig;
|
|
24
26
|
//#endregion
|
|
25
|
-
export { defineEval };
|
|
27
|
+
export { defineEval, defineEvalConfig };
|
|
26
28
|
//# sourceMappingURL=define-eval.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-eval.d.ts","names":[],"sources":["../../src/evals/define-eval.ts"],"mappings":";;;;;AAqBA
|
|
1
|
+
{"version":3,"file":"define-eval.d.ts","names":[],"sources":["../../src/evals/define-eval.ts"],"mappings":";;;;;AAqBA;;;;;;;;;AAQA;;;;;;;;iBARgB,UAAA,CAAW,GAAA,EAAK,cAAA,GAAiB,cAAA;;iBAQjC,gBAAA,CAAiB,MAAA,EAAQ,UAAA,GAAa,UAAA"}
|
|
@@ -22,7 +22,11 @@ function defineEval(def) {
|
|
|
22
22
|
if (typeof def.test !== "function") throw new Error("defineEval: `test` must be a function");
|
|
23
23
|
return def;
|
|
24
24
|
}
|
|
25
|
+
/** Define per-directory eval config. Default-export from `evals.config.ts`. */
|
|
26
|
+
function defineEvalConfig(config) {
|
|
27
|
+
return config;
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
//#endregion
|
|
27
|
-
export { defineEval };
|
|
31
|
+
export { defineEval, defineEvalConfig };
|
|
28
32
|
//# sourceMappingURL=define-eval.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-eval.js","names":[],"sources":["../../src/evals/define-eval.ts"],"sourcesContent":["import type { EvalDefinition } from \"./types\";\n\n/**\n * Define an agent eval. Default-export the result from a\n * `server/agents/<id>/evals/*.eval.ts` file.\n *\n * @example\n * ```ts\n * import { defineEval, includes } from \"@databricks/appkit/beta\";\n *\n * export default defineEval({\n * description: \"Weather agent basic coverage\",\n * async test(t) {\n * await t.send(\"What's the weather in Brooklyn?\");\n * t.succeeded();\n * t.calledTool(\"get_weather\");\n * t.check(t.reply, includes(\"Sunny\"));\n * },\n * });\n * ```\n */\nexport function defineEval(def: EvalDefinition): EvalDefinition {\n if (typeof def.test !== \"function\") {\n throw new Error(\"defineEval: `test` must be a function\");\n }\n return def;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,WAAW,KAAqC;AAC9D,KAAI,OAAO,IAAI,SAAS,WACtB,OAAM,IAAI,MAAM,wCAAwC;AAE1D,QAAO"}
|
|
1
|
+
{"version":3,"file":"define-eval.js","names":[],"sources":["../../src/evals/define-eval.ts"],"sourcesContent":["import type { EvalConfig, EvalDefinition } from \"./types\";\n\n/**\n * Define an agent eval. Default-export the result from a\n * `server/agents/<id>/evals/*.eval.ts` file.\n *\n * @example\n * ```ts\n * import { defineEval, includes } from \"@databricks/appkit/beta\";\n *\n * export default defineEval({\n * description: \"Weather agent basic coverage\",\n * async test(t) {\n * await t.send(\"What's the weather in Brooklyn?\");\n * t.succeeded();\n * t.calledTool(\"get_weather\");\n * t.check(t.reply, includes(\"Sunny\"));\n * },\n * });\n * ```\n */\nexport function defineEval(def: EvalDefinition): EvalDefinition {\n if (typeof def.test !== \"function\") {\n throw new Error(\"defineEval: `test` must be a function\");\n }\n return def;\n}\n\n/** Define per-directory eval config. Default-export from `evals.config.ts`. */\nexport function defineEvalConfig(config: EvalConfig): EvalConfig {\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,WAAW,KAAqC;AAC9D,KAAI,OAAO,IAAI,SAAS,WACtB,OAAM,IAAI,MAAM,wCAAwC;AAE1D,QAAO;;;AAIT,SAAgB,iBAAiB,QAAgC;AAC/D,QAAO"}
|
package/dist/evals/discover.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ interface DiscoveredEval {
|
|
|
8
8
|
/** The agent id (the `server/agents/<agent>` directory name). */
|
|
9
9
|
agent: string;
|
|
10
10
|
}
|
|
11
|
+
/** A per-agent `evals.config.ts` found under `server/agents/<agent>/evals/`. */
|
|
12
|
+
interface DiscoveredEvalConfig {
|
|
13
|
+
/** Absolute path to the `evals.config.ts` file. */
|
|
14
|
+
file: string;
|
|
15
|
+
/** The agent id whose evals this config applies to. */
|
|
16
|
+
agent: string;
|
|
17
|
+
}
|
|
11
18
|
/**
|
|
12
19
|
* Discover evals under `<rootDir>/server/agents/<agent>/evals/` — co-located
|
|
13
20
|
* with each agent's `agent.{md,ts}` (same folder-per-agent layout the agents
|
|
@@ -15,6 +22,13 @@ interface DiscoveredEval {
|
|
|
15
22
|
* path relative to that evals dir with `.eval.ts` stripped. Sorted + stable.
|
|
16
23
|
*/
|
|
17
24
|
declare function discoverEvalFiles(rootDir: string): DiscoveredEval[];
|
|
25
|
+
/**
|
|
26
|
+
* Discover the per-agent `evals.config.ts` (from {@link defineEvalConfig}) at
|
|
27
|
+
* `<rootDir>/server/agents/<agent>/evals/evals.config.ts`. Config is per-agent:
|
|
28
|
+
* each agent's config applies only to that agent's evals. Agents without a
|
|
29
|
+
* config file are omitted. Returns a stable, sorted list.
|
|
30
|
+
*/
|
|
31
|
+
declare function discoverEvalConfigs(rootDir: string): DiscoveredEvalConfig[];
|
|
18
32
|
//#endregion
|
|
19
|
-
export { DiscoveredEval, discoverEvalFiles };
|
|
33
|
+
export { DiscoveredEval, DiscoveredEvalConfig, discoverEvalConfigs, discoverEvalFiles };
|
|
20
34
|
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover.d.ts","names":[],"sources":["../../src/evals/discover.ts"],"mappings":";;UAOiB,cAAA;EAAc;EAE7B,IAAA;EAF6B;EAI7B,EAAA;EAAA;EAEA,KAAA;AAAA;;
|
|
1
|
+
{"version":3,"file":"discover.d.ts","names":[],"sources":["../../src/evals/discover.ts"],"mappings":";;UAOiB,cAAA;EAAc;EAE7B,IAAA;EAF6B;EAI7B,EAAA;EAAA;EAEA,KAAA;AAAA;;UAIe,oBAAA;EAAoB;EAEnC,IAAA;EAAA;EAEA,KAAA;AAAA;;;;;AAsDF;;iBAlCgB,iBAAA,CAAkB,OAAA,WAAkB,cAAA;;;;;;;iBAkCpC,mBAAA,CAAoB,OAAA,WAAkB,oBAAA"}
|
package/dist/evals/discover.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { agentDirNames } from "../core/agent/agent-dirs.js";
|
|
2
2
|
import { CODE_AGENTS_SOURCE_DIR } from "../core/agent/load-code-agents.js";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { readdirSync } from "node:fs";
|
|
4
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
5
5
|
|
|
6
6
|
//#region src/evals/discover.ts
|
|
7
7
|
/** Recursively collect `*.eval.ts` files under `dir`. Empty when `dir` is absent. */
|
|
@@ -43,7 +43,31 @@ function discoverEvalFiles(rootDir) {
|
|
|
43
43
|
}
|
|
44
44
|
return out.sort((a, b) => a.agent.localeCompare(b.agent) || a.id.localeCompare(b.id));
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Discover the per-agent `evals.config.ts` (from {@link defineEvalConfig}) at
|
|
48
|
+
* `<rootDir>/server/agents/<agent>/evals/evals.config.ts`. Config is per-agent:
|
|
49
|
+
* each agent's config applies only to that agent's evals. Agents without a
|
|
50
|
+
* config file are omitted. Returns a stable, sorted list.
|
|
51
|
+
*/
|
|
52
|
+
function discoverEvalConfigs(rootDir) {
|
|
53
|
+
const agentsDir = path.join(rootDir, CODE_AGENTS_SOURCE_DIR);
|
|
54
|
+
const out = [];
|
|
55
|
+
let entries;
|
|
56
|
+
try {
|
|
57
|
+
entries = readdirSync(agentsDir, { withFileTypes: true });
|
|
58
|
+
} catch {
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
for (const agent of agentDirNames(entries)) {
|
|
62
|
+
const file = path.join(agentsDir, agent, "evals", "evals.config.ts");
|
|
63
|
+
if (existsSync(file)) out.push({
|
|
64
|
+
file,
|
|
65
|
+
agent
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return out.sort((a, b) => a.agent.localeCompare(b.agent));
|
|
69
|
+
}
|
|
46
70
|
|
|
47
71
|
//#endregion
|
|
48
|
-
export { discoverEvalFiles };
|
|
72
|
+
export { discoverEvalConfigs, discoverEvalFiles };
|
|
49
73
|
//# sourceMappingURL=discover.js.map
|