@databricks/appkit 0.72.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.
Files changed (156) hide show
  1. package/CLAUDE.md +24 -0
  2. package/NOTICE.md +1 -0
  3. package/dist/appkit/package.js +1 -1
  4. package/dist/beta.d.ts +11 -8
  5. package/dist/beta.js +7 -6
  6. package/dist/cli/commands/agent/eval.js +85 -16
  7. package/dist/cli/commands/agent/eval.js.map +1 -1
  8. package/dist/connectors/index.js +1 -1
  9. package/dist/connectors/mlflow/auth.d.ts +11 -1
  10. package/dist/connectors/mlflow/auth.d.ts.map +1 -1
  11. package/dist/connectors/mlflow/auth.js +22 -2
  12. package/dist/connectors/mlflow/auth.js.map +1 -1
  13. package/dist/connectors/mlflow/index.d.ts +2 -0
  14. package/dist/database/errors.js +15 -5
  15. package/dist/database/errors.js.map +1 -1
  16. package/dist/database/runtime/data-path.d.ts +7 -0
  17. package/dist/database/runtime/data-path.d.ts.map +1 -0
  18. package/dist/database/runtime/data-path.js.map +1 -1
  19. package/dist/database/runtime/engine/drizzle-data-path.js +7 -5
  20. package/dist/database/runtime/engine/drizzle-data-path.js.map +1 -1
  21. package/dist/database/schema-builder/define-schema.d.ts +1 -1
  22. package/dist/database/schema-builder/define-schema.js +1 -1
  23. package/dist/database/schema-builder/define-schema.js.map +1 -1
  24. package/dist/errors/database-validation.d.ts +23 -0
  25. package/dist/errors/database-validation.d.ts.map +1 -0
  26. package/dist/errors/database-validation.js +24 -0
  27. package/dist/errors/database-validation.js.map +1 -0
  28. package/dist/errors/index.js +1 -0
  29. package/dist/evals/dataset.d.ts +49 -0
  30. package/dist/evals/dataset.d.ts.map +1 -0
  31. package/dist/evals/dataset.js +51 -0
  32. package/dist/evals/dataset.js.map +1 -0
  33. package/dist/evals/define-eval.d.ts +4 -2
  34. package/dist/evals/define-eval.d.ts.map +1 -1
  35. package/dist/evals/define-eval.js +5 -1
  36. package/dist/evals/define-eval.js.map +1 -1
  37. package/dist/evals/discover.d.ts +15 -1
  38. package/dist/evals/discover.d.ts.map +1 -1
  39. package/dist/evals/discover.js +26 -2
  40. package/dist/evals/discover.js.map +1 -1
  41. package/dist/evals/http-driver.d.ts.map +1 -1
  42. package/dist/evals/http-driver.js +82 -55
  43. package/dist/evals/http-driver.js.map +1 -1
  44. package/dist/evals/index.d.ts +14 -0
  45. package/dist/evals/index.js +6 -5
  46. package/dist/evals/judge.d.ts +1 -0
  47. package/dist/evals/judge.d.ts.map +1 -1
  48. package/dist/evals/mlflow-report.d.ts +1 -0
  49. package/dist/evals/mlflow-report.d.ts.map +1 -1
  50. package/dist/evals/mlflow-run.d.ts +2 -0
  51. package/dist/evals/mlflow-run.d.ts.map +1 -1
  52. package/dist/evals/report.d.ts +16 -1
  53. package/dist/evals/report.d.ts.map +1 -1
  54. package/dist/evals/report.js +64 -2
  55. package/dist/evals/report.js.map +1 -1
  56. package/dist/evals/run-eval.d.ts +8 -0
  57. package/dist/evals/run-eval.d.ts.map +1 -1
  58. package/dist/evals/run-eval.js +54 -5
  59. package/dist/evals/run-eval.js.map +1 -1
  60. package/dist/evals/run-evals.d.ts +41 -3
  61. package/dist/evals/run-evals.d.ts.map +1 -1
  62. package/dist/evals/run-evals.js +215 -34
  63. package/dist/evals/run-evals.js.map +1 -1
  64. package/dist/evals/types.d.ts +80 -6
  65. package/dist/evals/types.d.ts.map +1 -1
  66. package/dist/index.d.ts +2 -1
  67. package/dist/index.js +2 -1
  68. package/dist/plugin/plugin.d.ts.map +1 -1
  69. package/dist/plugin/plugin.js +1 -1
  70. package/dist/plugin/plugin.js.map +1 -1
  71. package/dist/plugins/database/crud/contract.js +17 -8
  72. package/dist/plugins/database/crud/contract.js.map +1 -1
  73. package/dist/plugins/database/crud/exposure.js +63 -22
  74. package/dist/plugins/database/crud/exposure.js.map +1 -1
  75. package/dist/plugins/database/crud/request.js +50 -0
  76. package/dist/plugins/database/crud/request.js.map +1 -0
  77. package/dist/plugins/database/crud/response.js +77 -0
  78. package/dist/plugins/database/crud/response.js.map +1 -0
  79. package/dist/plugins/database/crud/routes.js +71 -52
  80. package/dist/plugins/database/crud/routes.js.map +1 -1
  81. package/dist/plugins/database/database.d.ts +6 -4
  82. package/dist/plugins/database/database.d.ts.map +1 -1
  83. package/dist/plugins/database/database.js +46 -16
  84. package/dist/plugins/database/database.js.map +1 -1
  85. package/dist/plugins/database/defaults.js +5 -1
  86. package/dist/plugins/database/defaults.js.map +1 -1
  87. package/dist/plugins/database/entity-client.js +143 -10
  88. package/dist/plugins/database/entity-client.js.map +1 -1
  89. package/dist/plugins/database/entity-types.d.ts +1 -1
  90. package/dist/plugins/database/hooks.d.ts +38 -0
  91. package/dist/plugins/database/hooks.d.ts.map +1 -0
  92. package/dist/plugins/database/index.d.ts +3 -2
  93. package/dist/plugins/database/lifecycle.js +67 -28
  94. package/dist/plugins/database/lifecycle.js.map +1 -1
  95. package/dist/plugins/database/scope.js +58 -0
  96. package/dist/plugins/database/scope.js.map +1 -0
  97. package/dist/plugins/database/types.d.ts +40 -12
  98. package/dist/plugins/database/types.d.ts.map +1 -1
  99. package/dist/plugins/server/index.js +2 -2
  100. package/dist/plugins/server/index.js.map +1 -1
  101. package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js +3 -3
  102. package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  103. package/dist/plugins/server/static-server.js +3 -3
  104. package/dist/plugins/server/static-server.js.map +1 -1
  105. package/dist/plugins/server/utils.js +3 -3
  106. package/dist/plugins/server/utils.js.map +1 -1
  107. package/dist/plugins/server/vite-dev-server.js +4 -4
  108. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  109. package/dist/shared/src/schemas/manifest.d.ts +87 -87
  110. package/dist/type-generator/database/generate.js +3 -3
  111. package/dist/type-generator/database/generate.js.map +1 -1
  112. package/dist/type-generator/migration.js +2 -2
  113. package/dist/type-generator/migration.js.map +1 -1
  114. package/dist/type-generator/serving/server-file-extractor.js +3 -3
  115. package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
  116. package/docs/api/appkit/Class.AppKitError.md +1 -0
  117. package/docs/api/appkit/Class.DatabaseValidationError.md +191 -0
  118. package/docs/api/appkit/Function.defineEvalConfig.md +18 -0
  119. package/docs/api/appkit/Function.defineSchema.md +1 -1
  120. package/docs/api/appkit/Function.discoverEvalConfigs.md +18 -0
  121. package/docs/api/appkit/Function.formatResultsJUnit.md +18 -0
  122. package/docs/api/appkit/Function.formatResultsJson.md +18 -0
  123. package/docs/api/appkit/Function.readEvalDataset.md +21 -0
  124. package/docs/api/appkit/Function.resolveWorkspaceClient.md +18 -0
  125. package/docs/api/appkit/Function.runWithRetries.md +28 -0
  126. package/docs/api/appkit/Function.userTurns.md +20 -0
  127. package/docs/api/appkit/Interface.AssertionHandle.md +1 -1
  128. package/docs/api/appkit/Interface.DatabaseValidationIssue.md +21 -0
  129. package/docs/api/appkit/Interface.DatasetRow.md +21 -0
  130. package/docs/api/appkit/Interface.DiscoveredEvalConfig.md +25 -0
  131. package/docs/api/appkit/Interface.DriveResult.md +28 -0
  132. package/docs/api/appkit/Interface.EntityMutationHooks.md +173 -0
  133. package/docs/api/appkit/Interface.EvalDefinition.md +50 -0
  134. package/docs/api/appkit/Interface.EvalDriver.md +26 -5
  135. package/docs/api/appkit/Interface.EvalResult.md +11 -0
  136. package/docs/api/appkit/Interface.EvalSummary.md +11 -0
  137. package/docs/api/appkit/Interface.HookApp.md +12 -0
  138. package/docs/api/appkit/Interface.HookContext.md +21 -0
  139. package/docs/api/appkit/Interface.ReadEvalDatasetOptions.md +34 -0
  140. package/docs/api/appkit/Interface.ReadSerializerContext.md +21 -0
  141. package/docs/api/appkit/Interface.RunEvalOptions.md +22 -0
  142. package/docs/api/appkit/Interface.RunEvalsOptions.md +45 -1
  143. package/docs/api/appkit/Interface.TestContext.md +67 -8
  144. package/docs/api/appkit/TypeAlias.DatabaseApiConfig.md +53 -0
  145. package/docs/api/appkit/TypeAlias.DatabaseApiWriteOperation.md +8 -0
  146. package/docs/api/appkit/TypeAlias.DatabaseApiWritesConfig.md +49 -0
  147. package/docs/api/appkit/TypeAlias.DatabaseExports.md +3 -3
  148. package/docs/api/appkit/TypeAlias.EntityHooks.md +25 -0
  149. package/docs/api/appkit/TypeAlias.IDatabaseConfig.md +16 -5
  150. package/docs/api/appkit/TypeAlias.ReadSerializer.md +19 -0
  151. package/docs/api/appkit/TypeAlias.TransactionClient.md +19 -0
  152. package/docs/api/appkit.md +142 -119
  153. package/docs/plugins/database.md +144 -0
  154. package/llms.txt +24 -0
  155. package/package.json +2 -2
  156. package/sbom.cdx.json +1 -1
package/CLAUDE.md CHANGED
@@ -48,6 +48,7 @@ npx @databricks/appkit docs <query>
48
48
  - [Analytics plugin](./docs/plugins/analytics.md): Enables SQL query execution against Databricks SQL Warehouses.
49
49
  - [Caching](./docs/plugins/caching.md): AppKit provides both global and plugin-level caching capabilities.
50
50
  - [Creating custom plugins](./docs/plugins/custom-plugins.md): If you need custom API routes or background logic, implement an AppKit plugin. The fastest way is to use the CLI:
51
+ - [Database plugin](./docs/plugins/database.md): This plugin is currently beta. APIs may change between minor releases. Import from @databricks/appkit/beta. See Plugin Stability Tiers.
51
52
  - [Execution context](./docs/plugins/execution-context.md): AppKit manages Databricks authentication via two contexts:
52
53
  - [Files plugin](./docs/plugins/files.md): File operations against Databricks Unity Catalog Volumes. Supports listing, reading, downloading, uploading, deleting, and previewing files with built-in caching, retry, and timeout handling via the execution interceptor pipeline.
53
54
  - [Genie plugin](./docs/plugins/genie.md): Integrates Databricks AI/BI Genie spaces into your AppKit application, enabling natural language data queries via a conversational interface.
@@ -68,6 +69,7 @@ npx @databricks/appkit docs <query>
68
69
  - [Class: AuthenticationError](./docs/api/appkit/Class.AuthenticationError.md): Error thrown when authentication fails.
69
70
  - [Class: ConfigurationError](./docs/api/appkit/Class.ConfigurationError.md): Error thrown when configuration is missing or invalid.
70
71
  - [Class: ConnectionError](./docs/api/appkit/Class.ConnectionError.md): Error thrown when a connection or network operation fails.
72
+ - [Class: DatabaseValidationError](./docs/api/appkit/Class.DatabaseValidationError.md): Deliberate validation failure raised by a database mutation hook. Generated
71
73
  - [Class: DatabricksAdapter](./docs/api/appkit/Class.DatabricksAdapter.md): Adapter that talks directly to Databricks Model Serving /invocations endpoint.
72
74
  - [Class: ExecutionError](./docs/api/appkit/Class.ExecutionError.md): Error thrown when an operation execution fails.
73
75
  - [Class: InitializationError](./docs/api/appkit/Class.InitializationError.md): Error thrown when a service or component is not properly initialized.
@@ -97,9 +99,11 @@ npx @databricks/appkit docs <query>
97
99
  - [Function: createWorkspaceClient()](./docs/api/appkit/Function.createWorkspaceClient.md): Construct an AppKit workspace client.
98
100
  - [Function: database()](./docs/api/appkit/Function.database.md): Create a typed database plugin registration for a finalized schema.
99
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.
100
103
  - [Function: defineManifest()](./docs/api/appkit/Function.defineManifest.md): Validates a raw manifest (typically a manifest.json import) against the
101
104
  - [Function: defineSchema()](./docs/api/appkit/Function.defineSchema.md): Compile one declared schema. The returned type keeps the table names the
102
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
103
107
  - [Function: discoverEvalFiles()](./docs/api/appkit/Function.discoverEvalFiles.md): Discover evals under /server/agents//evals/ — co-located
104
108
  - [Function: enumColumn()](./docs/api/appkit/Function.enumColumn.md): Parameters
105
109
  - [Function: equals()](./docs/api/appkit/Function.equals.md): Passes when the value equals expected exactly.
@@ -111,6 +115,8 @@ npx @databricks/appkit docs <query>
111
115
  - [Function: formatEvalDetail()](./docs/api/appkit/Function.formatEvalDetail.md): Indented detail lines for a failing eval (error + failing assertions).
112
116
  - [Function: formatEvalHeadline()](./docs/api/appkit/Function.formatEvalHeadline.md): The one-line header for a single eval result (no failure detail).
113
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
114
120
  - [Function: formatSummaryLine()](./docs/api/appkit/Function.formatSummaryLine.md): The final PASS/FAIL summary line.
115
121
  - [Function: fromSupervisorApi()](./docs/api/appkit/Function.fromSupervisorApi.md): Creates an AgentAdapter backed by the Databricks AI Gateway
116
122
  - [Function: functionToolToDefinition()](./docs/api/appkit/Function.functionToolToDefinition.md): Parameters
@@ -138,17 +144,21 @@ npx @databricks/appkit docs <query>
138
144
  - [Function: mcpServer()](./docs/api/appkit/Function.mcpServer.md): Factory for declaring a custom MCP server tool.
139
145
  - [Function: normalizeHost()](./docs/api/appkit/Function.normalizeHost.md): Ensure the host has a scheme (Databricks env often lacks https://).
140
146
  - [Function: parseTextToolCalls()](./docs/api/appkit/Function.parseTextToolCalls.md): Parses text-based tool calls from model output.
147
+ - [Function: readEvalDataset()](./docs/api/appkit/Function.readEvalDataset.md): Read a Databricks managed evaluation dataset (a Unity Catalog table with
141
148
  - [Function: reportToMlflow()](./docs/api/appkit/Function.reportToMlflow.md): Write one pass/fail assessment per eval result to the Databricks MLflow REST
142
149
  - [Function: resolveDatabricksAuth()](./docs/api/appkit/Function.resolveDatabricksAuth.md): Parameters
143
150
  - [Function: resolveHostedTools()](./docs/api/appkit/Function.resolveHostedTools.md): Parameters
151
+ - [Function: resolveWorkspaceClient()](./docs/api/appkit/Function.resolveWorkspaceClient.md): Construct a Databricks WorkspaceClient for the eval runner — the object the
144
152
  - [Function: runAgent()](./docs/api/appkit/Function.runAgent.md): Standalone agent execution without createApp. Resolves the adapter, binds
145
153
  - [Function: runEval()](./docs/api/appkit/Function.runEval.md): Run a single eval against a driver. Never throws for assertion or agent
146
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
147
156
  - [Function: summarize()](./docs/api/appkit/Function.summarize.md): Parameters
148
157
  - [Function: text()](./docs/api/appkit/Function.text.md): Returns
149
158
  - [Function: timestamp()](./docs/api/appkit/Function.timestamp.md): Parameters
150
159
  - [Function: tool()](./docs/api/appkit/Function.tool.md): Factory for defining function tools with Zod schemas.
151
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
152
162
  - [Function: uuid()](./docs/api/appkit/Function.uuid.md): Returns
153
163
  - [Function: varchar()](./docs/api/appkit/Function.varchar.md): Parameters
154
164
  - [Interface: AgentAdapter](./docs/api/appkit/Interface.AgentAdapter.md): Properties
@@ -166,10 +176,14 @@ npx @databricks/appkit docs <query>
166
176
  - [Interface: CustomJudgeSpec](./docs/api/appkit/Interface.CustomJudgeSpec.md): A custom LLM-judge definition: a prompt template and choice→score mapping.
167
177
  - [Interface: DatabaseCredential](./docs/api/appkit/Interface.DatabaseCredential.md): Database credentials with OAuth token for Postgres connection
168
178
  - [Interface: DatabaseRegistry](./docs/api/appkit/Interface.DatabaseRegistry.md): CANONICAL augmentation target. Empty by default; the generated database.d.ts
179
+ - [Interface: DatabaseValidationIssue](./docs/api/appkit/Interface.DatabaseValidationIssue.md): One rejected field; path names public columns, never their values.
169
180
  - [Interface: DatabricksAuth](./docs/api/appkit/Interface.DatabricksAuth.md): Resolved Databricks host + bearer token for the eval runner's REST calls.
181
+ - [Interface: DatasetRow](./docs/api/appkit/Interface.DatasetRow.md): One row of a managed evaluation dataset. inputs are the kwargs passed to the
170
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/.
171
184
  - [Interface: DriveResult](./docs/api/appkit/Interface.DriveResult.md): What a driver returns for a single t.send.
172
185
  - [Interface: EndpointConfig](./docs/api/appkit/Interface.EndpointConfig.md): Properties
186
+ - [Interface: EntityMutationHooks<TTable>](./docs/api/appkit/Interface.EntityMutationHooks.md): Mutation lifecycle for one entity. A before hook may return a replacement
173
187
  - [Interface: EvalDefinition](./docs/api/appkit/Interface.EvalDefinition.md): A single eval, default-exported from a *.eval.ts file.
174
188
  - [Interface: EvalDriver](./docs/api/appkit/Interface.EvalDriver.md): Abstraction over how the agent is driven. The HTTP driver posts to a running
175
189
  - [Interface: EvalResult](./docs/api/appkit/Interface.EvalResult.md): The outcome of running one eval.
@@ -180,6 +194,8 @@ npx @databricks/appkit docs <query>
180
194
  - [Interface: FunctionTool](./docs/api/appkit/Interface.FunctionTool.md): Properties
181
195
  - [Interface: GenerateDatabaseCredentialRequest](./docs/api/appkit/Interface.GenerateDatabaseCredentialRequest.md): Request parameters for generating database OAuth credentials
182
196
  - [Interface: GenerationParams](./docs/api/appkit/Interface.GenerationParams.md): Optional generation parameters forwarded to the OpenAI-compatible serving
197
+ - [Interface: HookApp](./docs/api/appkit/Interface.HookApp.md): The only capability a hook receives: entities bound to its transaction.
198
+ - [Interface: HookContext](./docs/api/appkit/Interface.HookContext.md): Which entity is being mutated, and the surface a hook may write through.
183
199
  - [Interface: HostedSupervisorTool](./docs/api/appkit/Interface.HostedSupervisorTool.md): Tagged record returned by every supervisorTools factory. The
184
200
  - [Interface: HttpDriverOptions](./docs/api/appkit/Interface.HttpDriverOptions.md): Properties
185
201
  - [Interface: IAiSearchConfig](./docs/api/appkit/Interface.IAiSearchConfig.md): Base configuration interface for AppKit plugins
@@ -201,6 +217,8 @@ npx @databricks/appkit docs <query>
201
217
  - [Interface: PluginToolkitProvider](./docs/api/appkit/Interface.PluginToolkitProvider.md): Minimum shape every entry in the Plugins map must expose. Core
202
218
  - [Interface: PostResult](./docs/api/appkit/Interface.PostResult.md): Structured result for a best-effort POST that must not throw.
203
219
  - [Interface: PromptContext](./docs/api/appkit/Interface.PromptContext.md): Context passed to baseSystemPrompt callbacks.
220
+ - [Interface: ReadEvalDatasetOptions](./docs/api/appkit/Interface.ReadEvalDatasetOptions.md): Properties
221
+ - [Interface: ReadSerializerContext](./docs/api/appkit/Interface.ReadSerializerContext.md): Which entity and generated operation produced the row being shaped.
204
222
  - [Interface: RegisteredAgent](./docs/api/appkit/Interface.RegisteredAgent.md): Properties
205
223
  - [Interface: ReportOutcome](./docs/api/appkit/Interface.ReportOutcome.md): Properties
206
224
  - [Interface: RequestedClaims](./docs/api/appkit/Interface.RequestedClaims.md): Optional claims for fine-grained Unity Catalog table permissions
@@ -242,7 +260,11 @@ npx @databricks/appkit docs <query>
242
260
  - [Type Alias: AgentToolsFn()](./docs/api/appkit/TypeAlias.AgentToolsFn.md): Function form of AgentDefinition.tools. Receives the typed
243
261
  - [Type Alias: BaseSystemPromptOption](./docs/api/appkit/TypeAlias.BaseSystemPromptOption.md)
244
262
  - [Type Alias: ConfigSchema](./docs/api/appkit/TypeAlias.ConfigSchema.md): Configuration schema definition for plugin config.
263
+ - [Type Alias: DatabaseApiConfig<TSchema>](./docs/api/appkit/TypeAlias.DatabaseApiConfig.md): Full generated CRUD for every declared table by default. Set false to disable
264
+ - [Type Alias: DatabaseApiWriteOperation](./docs/api/appkit/TypeAlias.DatabaseApiWriteOperation.md): Generated HTTP write operations.
265
+ - [Type Alias: DatabaseApiWritesConfig<TSchema>](./docs/api/appkit/TypeAlias.DatabaseApiWritesConfig.md): All writes by default; false keeps reads only, and an object narrows writes.
245
266
  - [Type Alias: DatabaseExports](./docs/api/appkit/TypeAlias.DatabaseExports.md): Typed database API published by the plugin.
267
+ - [Type Alias: EntityHooks<TTable>](./docs/api/appkit/TypeAlias.EntityHooks.md): Response shaping and mutation lifecycle declared for one table.
246
268
  - [Type Alias: EvalProgress](./docs/api/appkit/TypeAlias.EvalProgress.md)
247
269
  - [Type Alias: ExecutionResult<T>](./docs/api/appkit/TypeAlias.ExecutionResult.md): Discriminated union for plugin execution results.
248
270
  - [Type Alias: FileAction](./docs/api/appkit/TypeAlias.FileAction.md): Every action the files plugin can perform.
@@ -254,6 +276,7 @@ npx @databricks/appkit docs <query>
254
276
  - [Type Alias: Matcher()](./docs/api/appkit/TypeAlias.Matcher.md): A deterministic matcher: inspects a string value and returns a result.
255
277
  - [Type Alias: PluginData<T, U, N>](./docs/api/appkit/TypeAlias.PluginData.md): Tuple of plugin class, config, and name. Created by toPlugin() and passed to createApp().
256
278
  - [Type Alias: Plugins](./docs/api/appkit/TypeAlias.Plugins.md): Plugin map passed to the function form of AgentDefinition.tools.
279
+ - [Type Alias: ReadSerializer()](./docs/api/appkit/TypeAlias.ReadSerializer.md): Shape one already private-safe row before it reaches the wire. A Promise
257
280
  - [Type Alias: ResolvedToolEntry](./docs/api/appkit/TypeAlias.ResolvedToolEntry.md): Internal tool-index entry after a tool record has been resolved to a dispatchable form.
258
281
  - [Type Alias: ResourceFieldEntry](./docs/api/appkit/TypeAlias.ResourceFieldEntry.md)
259
282
  - [Type Alias: ResourcePermission](./docs/api/appkit/TypeAlias.ResourcePermission.md): Union of all possible permission levels across all resource types.
@@ -263,6 +286,7 @@ npx @databricks/appkit docs <query>
263
286
  - [Type Alias: SupervisorTool](./docs/api/appkit/TypeAlias.SupervisorTool.md): Tools supported by the Databricks AI Gateway Responses API. The shapes match
264
287
  - [Type Alias: ToolRegistry](./docs/api/appkit/TypeAlias.ToolRegistry.md)
265
288
  - [Type Alias: ToPlugin()<T, U, N>](./docs/api/appkit/TypeAlias.ToPlugin.md): Factory function type returned by toPlugin(). Accepts optional config and returns a PluginData tuple.
289
+ - [Type Alias: TransactionClient](./docs/api/appkit/TypeAlias.TransactionClient.md): Entity and SQL capabilities bound to one transaction.
266
290
  - [Variable: agents](./docs/api/appkit/Variable.agents.md): Plugin factory for the agents plugin. Discovers agents from
267
291
  - [Variable: aiSearch](./docs/api/appkit/Variable.aiSearch.md)
268
292
  - [Variable: READ_ACTIONS](./docs/api/appkit/Variable.READ_ACTIONS.md): Actions that only read data.
package/NOTICE.md CHANGED
@@ -54,6 +54,7 @@ This Software contains code from the following open source projects:
54
54
  | [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table) | 8.21.3 | MIT | https://tanstack.com/table |
55
55
  | [@types/semver](https://www.npmjs.com/package/@types/semver) | 7.7.1 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/semver |
56
56
  | [apache-arrow](https://www.npmjs.com/package/apache-arrow) | 21.1.0 | Apache-2.0 | https://arrow.apache.org/js/ |
57
+ | [autoevals](https://www.npmjs.com/package/autoevals) | 0.3.0 | MIT | https://www.braintrust.dev/docs |
57
58
  | [class-variance-authority](https://www.npmjs.com/package/class-variance-authority) | 0.7.1 | Apache-2.0 | https://github.com/joe-bell/cva#readme |
58
59
  | [clsx](https://www.npmjs.com/package/clsx) | 2.1.1 | MIT | https://github.com/lukeed/clsx#readme |
59
60
  | [cmdk](https://www.npmjs.com/package/cmdk) | 1.1.1 | MIT | https://github.com/pacocoursey/cmdk#readme |
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "@databricks/appkit";
3
- var version = "0.72.0";
3
+ var version = "0.74.0";
4
4
 
5
5
  //#endregion
6
6
  export { name, version };
package/dist/beta.d.ts CHANGED
@@ -15,26 +15,29 @@ import { Schema } from "./database/schema-builder/types.js";
15
15
  import { bigid, bigint, boolean, enumColumn, id, integer, jsonb, text, timestamp, uuid, varchar } from "./database/schema-builder/columns.js";
16
16
  import { defineSchema } from "./database/schema-builder/define-schema.js";
17
17
  import { fk } from "./database/schema-builder/fk.js";
18
- import { DatabricksAuth, ResolveDatabricksAuthOptions, resolveDatabricksAuth } from "./connectors/mlflow/auth.js";
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, userTurns } from "./evals/dataset.js";
20
21
  import { AssertionHandle, AssertionResult, CustomJudgeSpec, DriveResult, EvalDefinition, EvalDriver, EvalResult, MatchResult, Matcher, Severity, TestContext } from "./evals/types.js";
21
- import { defineEval } from "./evals/define-eval.js";
22
- 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";
23
24
  import { HttpDriverOptions, createHttpDriver } from "./evals/http-driver.js";
24
25
  import { JudgeConfig, JudgeScore, configureJudge, isJudgeConfigured } from "./evals/judge.js";
25
26
  import { equals, includes, matches } from "./evals/matchers.js";
26
27
  import { Assessment, ReportOutcome, buildAssessments, reportToMlflow } from "./evals/mlflow-report.js";
27
- 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";
28
29
  import { RunEvalOptions, runEval } from "./evals/run-eval.js";
29
- import { EvalProgress, EvalRunSummary, RunEvalsOptions, runEvalsInDir } from "./evals/run-evals.js";
30
+ import { EvalProgress, EvalRunSummary, RunEvalsOptions, runEvalsInDir, runWithRetries } from "./evals/run-evals.js";
31
+ import "./evals/index.js";
30
32
  import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "./core/agent/load-agents.js";
31
33
  import { agents } from "./plugins/agents/agents.js";
32
34
  import "./plugins/agents/index.js";
33
35
  import { IAiSearchConfig, IndexConfig, RerankerConfig, SearchFilters, SearchRequest, SearchResponse, SearchResult } from "./plugins/ai-search/types.js";
34
36
  import { aiSearch } from "./plugins/ai-search/ai-search.js";
35
- import { DatabaseExports } from "./plugins/database/entity-types.js";
36
- import { IDatabaseConfig } from "./plugins/database/types.js";
37
+ import { DatabaseExports, TransactionClient } from "./plugins/database/entity-types.js";
38
+ import { EntityMutationHooks, HookApp, HookContext } from "./plugins/database/hooks.js";
39
+ import { DatabaseApiConfig, DatabaseApiWriteOperation, DatabaseApiWritesConfig, EntityHooks, IDatabaseConfig, ReadSerializer, ReadSerializerContext } from "./plugins/database/types.js";
37
40
  import { database } from "./plugins/database/database.js";
38
41
  import "./plugins/database/index.js";
39
42
  import "./plugins/beta-exports.generated.js";
40
- 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 DatabaseExports, DatabricksAdapter, DatabricksAuth, DiscoveredEval, DriveResult, EvalDefinition, EvalDriver, EvalProgress, EvalResult, EvalRunSummary, EvalSummary, type FunctionTool, type GenerationParams, 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, 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 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, reportToMlflow, resolveDatabricksAuth, resolveHostedTools, 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
@@ -1,5 +1,5 @@
1
1
  import { AppKitMcpClient } from "./connectors/mcp/client.js";
2
- import { resolveDatabricksAuth } from "./connectors/mlflow/auth.js";
2
+ import { resolveDatabricksAuth, resolveWorkspaceClient } from "./connectors/mlflow/auth.js";
3
3
  import { MlflowClient, normalizeHost } from "./connectors/mlflow/client.js";
4
4
  import { tool } from "./core/agent/tools/tool.js";
5
5
  import { defineTool, executeFromRegistry, toolsFromRegistry } from "./core/agent/tools/define-tool.js";
@@ -15,15 +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 { defineEval } from "./evals/define-eval.js";
19
- 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";
20
21
  import { createHttpDriver } from "./evals/http-driver.js";
21
22
  import { configureJudge, isJudgeConfigured } from "./evals/judge.js";
22
23
  import { equals, includes, matches } from "./evals/matchers.js";
23
24
  import { buildAssessments, reportToMlflow } from "./evals/mlflow-report.js";
24
- 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";
25
26
  import { runEval } from "./evals/run-eval.js";
26
- import { runEvalsInDir } from "./evals/run-evals.js";
27
+ import { runEvalsInDir, runWithRetries } from "./evals/run-evals.js";
27
28
  import "./evals/index.js";
28
29
  import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "./core/agent/load-agents.js";
29
30
  import { agents } from "./plugins/agents/agents.js";
@@ -32,4 +33,4 @@ import { aiSearch } from "./plugins/ai-search/ai-search.js";
32
33
  import { database } from "./plugins/database/database.js";
33
34
  import "./plugins/beta-exports.generated.js";
34
35
 
35
- 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, reportToMlflow, resolveDatabricksAuth, resolveHostedTools, 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 { Command } from "commander";
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
- /** Progress reporter: stream each eval as it runs instead of going silent. */
51
- function makeProgressReporter(runner, url) {
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
- console.log(`Running ${event.total} eval${event.total === 1 ? "" : "s"} against ${url}\n`);
71
+ info(`Running ${event.total} eval${event.total === 1 ? "" : "s"} against ${url}\n`);
56
72
  break;
57
73
  case "run-created":
58
- console.log(`MLflow evaluation run: ${event.runId}\n`);
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,45 +84,97 @@ function makeProgressReporter(runner, url) {
67
84
  function formatFailureLine(f) {
68
85
  return ` ✗ trace ${f.traceId}: ${f.status ?? ""} ${f.error ?? ""}`.trim();
69
86
  }
70
- /** Print the MLflow assessment/finish outcome after a run that created one. */
71
- function printMlflowOutcome(mlflow) {
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
- console.log(`MLflow: ${report.written} assessment(s) written` + (report.skipped ? `, ${report.skipped} skipped` : "") + (report.failures.length ? `, ${report.failures.length} failed` : ""));
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 auth = await runner.resolveDatabricksAuth({
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) ?? {};
107
+ const warehouseId = opts.warehouseId ?? process.env.DATABRICKS_WAREHOUSE_ID;
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
+ };
85
127
  let summary;
86
128
  try {
87
129
  summary = await runner.runEvalsInDir({
88
130
  rootDir: opts.root,
89
131
  baseUrl: opts.url,
90
132
  filter,
133
+ tags: opts.tag,
91
134
  strict: opts.strict,
92
135
  headers: opts.header ? parseHeaders(opts.header) : void 0,
93
136
  concurrency: opts.concurrency,
94
137
  mlflow: resolveMlflow(opts, auth),
95
138
  judge: resolveJudge(opts, auth),
96
- onEvent: makeProgressReporter(runner, opts.url)
139
+ workspaceClient,
140
+ warehouseId,
141
+ timeoutMs,
142
+ retries,
143
+ onEvent: makeProgressReporter(runner, opts.url, machine, info)
97
144
  });
98
145
  } catch (err) {
99
146
  console.error(`\nEval run failed: ${err instanceof Error ? err.message : String(err)}`);
100
147
  process.exitCode = 1;
101
148
  return;
102
149
  }
103
- console.log(`\n${runner.formatSummaryLine(summary.results)}`);
104
- if (summary.mlflow) printMlflowOutcome(summary.mlflow);
105
- else console.log("\nMLflow evaluation run skipped — pass --experiment (or set MLFLOW_EXPERIMENT_ID) plus --profile/--databricks-host to create one.");
106
- if (!runner.summarize(summary.results).allPassed) process.exitCode = 1;
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;
107
172
  }
108
- 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 writing assessments to UC-backed experiments (default: MLFLOW_TRACING_SQL_WAREHOUSE_ID or DATABRICKS_WAREHOUSE_ID)").option("--judge-model <endpoint>", "Databricks serving endpoint to use as the LLM judge for t.judge.* (default: APPKIT_JUDGE_MODEL)").action(runAgentEval);
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);
109
178
 
110
179
  //#endregion
111
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 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 formatEvalHeadline(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 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 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 writing assessments to UC-backed experiments (default: MLFLOW_TRACING_SQL_WAREHOUSE_ID or DATABRICKS_WAREHOUSE_ID)\",\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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"}
@@ -13,7 +13,7 @@ import "./jobs/index.js";
13
13
  import { buildMcpHostPolicy } from "./mcp/host-policy.js";
14
14
  import { AppKitMcpClient } from "./mcp/client.js";
15
15
  import "./mcp/index.js";
16
- import { resolveDatabricksAuth } from "./mlflow/auth.js";
16
+ import { resolveDatabricksAuth, resolveWorkspaceClient } from "./mlflow/auth.js";
17
17
  import { MlflowClient, normalizeHost } from "./mlflow/client.js";
18
18
  import { DEFAULT_WAREHOUSE_STARTUP_TIMEOUT_MS, SQLWarehouseConnector } from "./sql-warehouse/client.js";
19
19
  import "./sql-warehouse/index.js";
@@ -1,3 +1,5 @@
1
+ import { WorkspaceClient } from "../../workspace-client/index.js";
2
+
1
3
  //#region src/connectors/mlflow/auth.d.ts
2
4
  /** Resolved Databricks host + bearer token for the eval runner's REST calls. */
3
5
  interface DatabricksAuth {
@@ -13,6 +15,14 @@ interface ResolveDatabricksAuthOptions {
13
15
  token?: string;
14
16
  }
15
17
  declare function resolveDatabricksAuth(options?: ResolveDatabricksAuthOptions): Promise<DatabricksAuth | undefined>;
18
+ /**
19
+ * Construct a Databricks `WorkspaceClient` for the eval runner — the object the
20
+ * SDK-backed connectors (e.g. `SQLWarehouseConnector`) take. An explicit
21
+ * host+token builds a PAT client; otherwise the profile (or ambient config) is
22
+ * used and the SDK resolves credentials, minting OAuth as needed. Returns
23
+ * `undefined` if construction throws (missing/invalid config).
24
+ */
25
+ declare function resolveWorkspaceClient(options?: ResolveDatabricksAuthOptions): WorkspaceClient | undefined;
16
26
  //#endregion
17
- export { DatabricksAuth, ResolveDatabricksAuthOptions, resolveDatabricksAuth };
27
+ export { DatabricksAuth, ResolveDatabricksAuthOptions, resolveDatabricksAuth, resolveWorkspaceClient };
18
28
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/connectors/mlflow/auth.ts"],"mappings":";;UAGiB,cAAA;EACf,IAAA;EACA,KAAA;AAAA;AAAA,UAGe,4BAAA;EAAA;EAEf,OAAA;;EAEA,IAAA;EAFA;EAIA,KAAA;AAAA;AAAA,iBA+CoB,qBAAA,CACpB,OAAA,GAAS,4BAAA,GACR,OAAA,CAAQ,cAAA"}
1
+ {"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/connectors/mlflow/auth.ts"],"mappings":";;;;UAMiB,cAAA;EACf,IAAA;EACA,KAAA;AAAA;AAAA,UAGe,4BAAA;EAHV;EAKL,OAAA;EAF2C;EAI3C,IAAA;EAJ2C;EAM3C,KAAA;AAAA;AAAA,iBA8CoB,qBAAA,CACpB,OAAA,GAAS,4BAAA,GACR,OAAA,CAAQ,cAAA;;;AAFX;;;;;iBAiBgB,sBAAA,CACd,OAAA,GAAS,4BAAA,GACR,eAAA"}
@@ -23,7 +23,8 @@ function extractBearer(headers) {
23
23
  */
24
24
  async function resolveViaSdk(options) {
25
25
  try {
26
- const client = createWorkspaceClient(options.profile ? { profile: options.profile } : {});
26
+ const client = resolveWorkspaceClient(options);
27
+ if (!client) return void 0;
27
28
  const headers = new Headers();
28
29
  await client.config.authenticate(headers);
29
30
  const token = options.token ?? extractBearer(headers);
@@ -44,7 +45,26 @@ async function resolveDatabricksAuth(options = {}) {
44
45
  };
45
46
  return resolveViaSdk(options);
46
47
  }
48
+ /**
49
+ * Construct a Databricks `WorkspaceClient` for the eval runner — the object the
50
+ * SDK-backed connectors (e.g. `SQLWarehouseConnector`) take. An explicit
51
+ * host+token builds a PAT client; otherwise the profile (or ambient config) is
52
+ * used and the SDK resolves credentials, minting OAuth as needed. Returns
53
+ * `undefined` if construction throws (missing/invalid config).
54
+ */
55
+ function resolveWorkspaceClient(options = {}) {
56
+ try {
57
+ if (options.host && options.token) return createWorkspaceClient({
58
+ host: options.host,
59
+ token: options.token,
60
+ authType: "pat"
61
+ });
62
+ return createWorkspaceClient(options.profile ? { profile: options.profile } : {});
63
+ } catch {
64
+ return;
65
+ }
66
+ }
47
67
 
48
68
  //#endregion
49
- export { resolveDatabricksAuth };
69
+ export { resolveDatabricksAuth, resolveWorkspaceClient };
50
70
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":[],"sources":["../../../src/connectors/mlflow/auth.ts"],"sourcesContent":["import { createWorkspaceClient } from \"../../workspace-client\";\n\n/** Resolved Databricks host + bearer token for the eval runner's REST calls. */\nexport interface DatabricksAuth {\n host: string;\n token: string;\n}\n\nexport interface ResolveDatabricksAuthOptions {\n /** `~/.databrickscfg` profile to authenticate with (e.g. `dogfood`). */\n profile?: string;\n /** Explicit host; wins over the profile/SDK-resolved host when set. */\n host?: string;\n /** Explicit bearer token; when set, no OAuth is minted (PAT/CI path). */\n token?: string;\n}\n\n/**\n * Resolve `{host, token}` for the eval runner the same way the rest of AppKit\n * authenticates: construct a Databricks `WorkspaceClient` and let its config\n * mint (and later refresh) an OAuth bearer from the CLI profile — no hand-set\n * PAT required. An explicit host/token still wins (PAT or CI env), so the SDK\n * is only consulted for whatever isn't supplied.\n *\n * Returns `undefined` when neither an explicit token nor a resolvable profile\n * yields a bearer, so the caller can treat auth as simply unavailable.\n */\n/** Pull the bearer out of an `Authorization: Bearer <token>` header. */\nfunction extractBearer(headers: Headers): string | undefined {\n return headers.get(\"authorization\")?.replace(/^Bearer\\s+/i, \"\");\n}\n\n/**\n * Resolve `{host, token}` via the SDK: construct a `WorkspaceClient`, let it\n * mint/refresh an OAuth bearer from the profile (or reuse a PAT), and fall back\n * to any explicit host/token the caller supplied. Returns `undefined` when\n * either is missing or the SDK can't resolve credentials.\n */\nasync function resolveViaSdk(\n options: ResolveDatabricksAuthOptions,\n): Promise<DatabricksAuth | undefined> {\n try {\n const client = createWorkspaceClient(\n options.profile ? { profile: options.profile } : {},\n );\n const headers = new Headers();\n // Mints the OAuth access token (or reuses a PAT from the profile) and adds\n // an `Authorization: Bearer <token>` header — the same call the connectors\n // use before each request.\n await client.config.authenticate(headers);\n const token = options.token ?? extractBearer(headers);\n const host =\n options.host ??\n (await client.config.getHost()).toString().replace(/\\/+$/, \"\");\n if (!token || !host) return undefined;\n return { host, token };\n } catch {\n return undefined;\n }\n}\n\nexport async function resolveDatabricksAuth(\n options: ResolveDatabricksAuthOptions = {},\n): Promise<DatabricksAuth | undefined> {\n // Fully explicit — no need to touch the SDK.\n if (options.host && options.token) {\n return { host: options.host, token: options.token };\n }\n return resolveViaSdk(options);\n}\n"],"mappings":";;;;;;;;;;;;;;AA4BA,SAAS,cAAc,SAAsC;AAC3D,QAAO,QAAQ,IAAI,gBAAgB,EAAE,QAAQ,eAAe,GAAG;;;;;;;;AASjE,eAAe,cACb,SACqC;AACrC,KAAI;EACF,MAAM,SAAS,sBACb,QAAQ,UAAU,EAAE,SAAS,QAAQ,SAAS,GAAG,EAAE,CACpD;EACD,MAAM,UAAU,IAAI,SAAS;AAI7B,QAAM,OAAO,OAAO,aAAa,QAAQ;EACzC,MAAM,QAAQ,QAAQ,SAAS,cAAc,QAAQ;EACrD,MAAM,OACJ,QAAQ,SACP,MAAM,OAAO,OAAO,SAAS,EAAE,UAAU,CAAC,QAAQ,QAAQ,GAAG;AAChE,MAAI,CAAC,SAAS,CAAC,KAAM,QAAO;AAC5B,SAAO;GAAE;GAAM;GAAO;SAChB;AACN;;;AAIJ,eAAsB,sBACpB,UAAwC,EAAE,EACL;AAErC,KAAI,QAAQ,QAAQ,QAAQ,MAC1B,QAAO;EAAE,MAAM,QAAQ;EAAM,OAAO,QAAQ;EAAO;AAErD,QAAO,cAAc,QAAQ"}
1
+ {"version":3,"file":"auth.js","names":[],"sources":["../../../src/connectors/mlflow/auth.ts"],"sourcesContent":["import {\n createWorkspaceClient,\n type WorkspaceClient,\n} from \"../../workspace-client\";\n\n/** Resolved Databricks host + bearer token for the eval runner's REST calls. */\nexport interface DatabricksAuth {\n host: string;\n token: string;\n}\n\nexport interface ResolveDatabricksAuthOptions {\n /** `~/.databrickscfg` profile to authenticate with (e.g. `dogfood`). */\n profile?: string;\n /** Explicit host; wins over the profile/SDK-resolved host when set. */\n host?: string;\n /** Explicit bearer token; when set, no OAuth is minted (PAT/CI path). */\n token?: string;\n}\n\n/**\n * Resolve `{host, token}` for the eval runner the same way the rest of AppKit\n * authenticates: construct a Databricks `WorkspaceClient` and let its config\n * mint (and later refresh) an OAuth bearer from the CLI profile — no hand-set\n * PAT required. An explicit host/token still wins (PAT or CI env), so the SDK\n * is only consulted for whatever isn't supplied.\n *\n * Returns `undefined` when neither an explicit token nor a resolvable profile\n * yields a bearer, so the caller can treat auth as simply unavailable.\n */\n/** Pull the bearer out of an `Authorization: Bearer <token>` header. */\nfunction extractBearer(headers: Headers): string | undefined {\n return headers.get(\"authorization\")?.replace(/^Bearer\\s+/i, \"\");\n}\n\n/**\n * Resolve `{host, token}` via the SDK: construct a `WorkspaceClient`, let it\n * mint/refresh an OAuth bearer from the profile (or reuse a PAT), and fall back\n * to any explicit host/token the caller supplied. Returns `undefined` when\n * either is missing or the SDK can't resolve credentials.\n */\nasync function resolveViaSdk(\n options: ResolveDatabricksAuthOptions,\n): Promise<DatabricksAuth | undefined> {\n try {\n const client = resolveWorkspaceClient(options);\n if (!client) return undefined;\n const headers = new Headers();\n // Mints the OAuth access token (or reuses a PAT from the profile) and adds\n // an `Authorization: Bearer <token>` header — the same call the connectors\n // use before each request.\n await client.config.authenticate(headers);\n const token = options.token ?? extractBearer(headers);\n const host =\n options.host ??\n (await client.config.getHost()).toString().replace(/\\/+$/, \"\");\n if (!token || !host) return undefined;\n return { host, token };\n } catch {\n return undefined;\n }\n}\n\nexport async function resolveDatabricksAuth(\n options: ResolveDatabricksAuthOptions = {},\n): Promise<DatabricksAuth | undefined> {\n // Fully explicit — no need to touch the SDK.\n if (options.host && options.token) {\n return { host: options.host, token: options.token };\n }\n return resolveViaSdk(options);\n}\n\n/**\n * Construct a Databricks `WorkspaceClient` for the eval runner — the object the\n * SDK-backed connectors (e.g. `SQLWarehouseConnector`) take. An explicit\n * host+token builds a PAT client; otherwise the profile (or ambient config) is\n * used and the SDK resolves credentials, minting OAuth as needed. Returns\n * `undefined` if construction throws (missing/invalid config).\n */\nexport function resolveWorkspaceClient(\n options: ResolveDatabricksAuthOptions = {},\n): WorkspaceClient | undefined {\n try {\n if (options.host && options.token) {\n return createWorkspaceClient({\n host: options.host,\n token: options.token,\n authType: \"pat\",\n });\n }\n return createWorkspaceClient(\n options.profile ? { profile: options.profile } : {},\n );\n } catch {\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA+BA,SAAS,cAAc,SAAsC;AAC3D,QAAO,QAAQ,IAAI,gBAAgB,EAAE,QAAQ,eAAe,GAAG;;;;;;;;AASjE,eAAe,cACb,SACqC;AACrC,KAAI;EACF,MAAM,SAAS,uBAAuB,QAAQ;AAC9C,MAAI,CAAC,OAAQ,QAAO;EACpB,MAAM,UAAU,IAAI,SAAS;AAI7B,QAAM,OAAO,OAAO,aAAa,QAAQ;EACzC,MAAM,QAAQ,QAAQ,SAAS,cAAc,QAAQ;EACrD,MAAM,OACJ,QAAQ,SACP,MAAM,OAAO,OAAO,SAAS,EAAE,UAAU,CAAC,QAAQ,QAAQ,GAAG;AAChE,MAAI,CAAC,SAAS,CAAC,KAAM,QAAO;AAC5B,SAAO;GAAE;GAAM;GAAO;SAChB;AACN;;;AAIJ,eAAsB,sBACpB,UAAwC,EAAE,EACL;AAErC,KAAI,QAAQ,QAAQ,QAAQ,MAC1B,QAAO;EAAE,MAAM,QAAQ;EAAM,OAAO,QAAQ;EAAO;AAErD,QAAO,cAAc,QAAQ;;;;;;;;;AAU/B,SAAgB,uBACd,UAAwC,EAAE,EACb;AAC7B,KAAI;AACF,MAAI,QAAQ,QAAQ,QAAQ,MAC1B,QAAO,sBAAsB;GAC3B,MAAM,QAAQ;GACd,OAAO,QAAQ;GACf,UAAU;GACX,CAAC;AAEJ,SAAO,sBACL,QAAQ,UAAU,EAAE,SAAS,QAAQ,SAAS,GAAG,EAAE,CACpD;SACK;AACN"}
@@ -0,0 +1,2 @@
1
+ import { DatabricksAuth, ResolveDatabricksAuthOptions, resolveDatabricksAuth, resolveWorkspaceClient } from "./auth.js";
2
+ import { MlflowClient, PostResult, normalizeHost } from "./client.js";