mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-30 01:53:45 +00:00
Describe billing_by_model on the API and the Billing tab
StageProjection.billing_by_model and a BilledModelUsage schema that reuses fabro's type; the TypeScript client regenerated; the Billing tab's token tooltip says subagent tokens are included and priced at each subagent's model; the stage.completed docs describe the rows and the one usage rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
850d5cca52
commit
36e9259eb7
10 changed files with 194 additions and 36 deletions
|
|
@ -97,6 +97,9 @@ function TokenBreakdown({ billing }: { billing: BilledTokenCounts }) {
|
|||
</Fragment>
|
||||
))}
|
||||
</dl>
|
||||
<p className="border-line text-fg-3 mt-1.5 border-t pt-1">
|
||||
Includes subagent tokens, priced at each subagent's model.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ Emitted when a workflow node finishes execution.
|
|||
| `usage.reasoning_tokens` | number? | Reasoning/thinking tokens |
|
||||
| `usage.speed` | string? | Speed tier |
|
||||
| `usage.cost` | number? | Estimated cost in USD |
|
||||
| `billing_by_model` | array? | For an agent stage, the stage's billing split by model: the root session's route and each subagent's own model, a subagent whose model the catalog does not know billed at the root's. Each row has `model`, `tokens`, and `total_usd_micros`, and the rows sum to the stage's billing. Empty for stages without a coding agent and on events written before it existed |
|
||||
| `error` | string? | Error message (flattened from failure detail) |
|
||||
| `failure_class` | string? | `"transient_infra"`, `"deterministic"`, `"budget_exhausted"`, `"compilation_loop"`, `"canceled"`, `"structural"` |
|
||||
| `failure_signature` | string? | Dedup key for repeated failures |
|
||||
|
|
@ -433,6 +434,12 @@ Emitted when a workflow node finishes execution.
|
|||
| `restart_failure_signatures` | object? | Restart failure signature counts |
|
||||
| `response` | string? | Full LLM or agent response text when produced by the stage |
|
||||
| `notes` | string? | Free-text notes |
|
||||
|
||||
An agent stage's usage is its whole session tree's: the root session and
|
||||
every subagent, live in `StageProjection.usage` and here at completion, both
|
||||
read from the same fold of the stage's agent events. The root is priced at
|
||||
its route and each subagent at its own model; where the provider reported a
|
||||
cost, that cost stands.
|
||||
| `files_touched` | string[] | File paths modified |
|
||||
| `attempt` | number | Attempt number (1-based) |
|
||||
| `max_attempts` | number | Maximum attempts allowed |
|
||||
|
|
|
|||
|
|
@ -11227,6 +11227,18 @@ components:
|
|||
agent_control:
|
||||
$ref: "#/components/schemas/AgentControlState"
|
||||
description: Whether the agent is executing normally or waiting for steering after an interrupt.
|
||||
billing_by_model:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/BilledModelUsage"
|
||||
default: []
|
||||
description: >-
|
||||
The completed stage's `usage` split by model, as `stage.completed`
|
||||
reported it: the root session's route and each subagent's own
|
||||
model, a subagent whose model the catalog does not know billed at
|
||||
the root's. Sums to `usage`. Empty while the stage runs and for
|
||||
stages without a coding agent; the billing rollup then bills
|
||||
`usage` to `model`.
|
||||
agent:
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/AgentSessionProjection"
|
||||
|
|
@ -13355,6 +13367,26 @@ components:
|
|||
description: Billed USD amount in micros.
|
||||
example: 720000
|
||||
|
||||
BilledModelUsage:
|
||||
description: >-
|
||||
Usage and cost billed to one model: one response, or one model's share
|
||||
of a stage.
|
||||
type: object
|
||||
required:
|
||||
- model
|
||||
- tokens
|
||||
properties:
|
||||
model:
|
||||
$ref: "#/components/schemas/BillingModelRef"
|
||||
tokens:
|
||||
$ref: "#/components/schemas/CompletionUsage"
|
||||
total_usd_micros:
|
||||
type: integer
|
||||
format: int64
|
||||
description: >-
|
||||
Cost for `tokens`, when the provider reported one or the catalog
|
||||
could price them. Absent means no cost data, not zero.
|
||||
|
||||
BillingModelRef:
|
||||
description: Provider-qualified billing model identity used for cost estimates.
|
||||
type: object
|
||||
|
|
|
|||
|
|
@ -385,6 +385,7 @@ fn main() {
|
|||
&[],
|
||||
),
|
||||
("StageProjection", "fabro_types::StageProjection", &[]),
|
||||
("BilledModelUsage", "fabro_types::BilledModelUsage", &[]),
|
||||
(
|
||||
"StageInferenceProjection",
|
||||
"fabro_types::StageInferenceProjection",
|
||||
|
|
|
|||
|
|
@ -39,39 +39,39 @@ pub mod types {
|
|||
};
|
||||
pub use fabro_types::{
|
||||
ActivatedSkill, AgentControlState, AgentEventProps, AgentMcpToolSummary,
|
||||
AgentToolsAvailableProps, AskFabro, AuthMethod, AutomationRef, BilledTokenCounts, BlobHash,
|
||||
CommandTermination, Conclusion, ContextWindowBreakdownItem, ContextWindowCategory,
|
||||
ContextWindowCountMethod, ContextWindowSnapshot, ContextWindowStaleness,
|
||||
ContextWindowWarning, CreateVariableRequest, DiffStats, DiffSummary, DirtyStatus,
|
||||
EventEnvelope, ExecOutputTail, FailureCategory, FailureDetail, FailureSignature,
|
||||
GitContext, GitRunTarget, GitRunTarget as AutomationGitWorkflowSource, IdpIdentity,
|
||||
IntegrationConnectionKind, IntegrationConnectionState, IntegrationConnectionStatus,
|
||||
IntegrationProvider, IntegrationStatus, InterviewOption, InterviewQuestionRecord,
|
||||
LlmOutputKind, McpServerDraft as CreateMcpServerRequest, McpServerProjection,
|
||||
McpServerReplace as ReplaceMcpServerRequest, McpServerStatus, McpServerView as McpServer,
|
||||
McpTransportView, Model, ModelControls, ModelCosts, ModelFeatures, ModelLimits,
|
||||
ModelRef as BillingModelRef, ModelTestMode, PairId, PairMessageId, PairMessageRecord,
|
||||
PairMessageRequest, PairRecord, PairStartRequest, PairStatus, PairTarget,
|
||||
PairTranscriptEntry, PairTranscriptResponse, ParallelBranchId, ParallelBranchResult,
|
||||
PendingInterviewRecord, PermissionLevel, Principal, Provider, PullRequest,
|
||||
PullRequestCreation, PullRequestCreationId, PullRequestCreationStatus, PullRequestDetails,
|
||||
PullRequestDetailsStatus, PullRequestDetailsUnavailableReason, PullRequestLink,
|
||||
PullRequestMeta, PullRequestResponse, QuestionType, RepositoryRef, ReviewTarget,
|
||||
ReviewTargetKind, Run, RunApproval, RunApprovalState, RunClientProvenance, RunEvent,
|
||||
RunEventDetailContentKind, RunEventDetailResponse, RunFailure, RunIntent, RunIntentArgs,
|
||||
RunPairStatusResponse, RunProjection, RunProvenance, RunRunnableSource, RunSandbox,
|
||||
RunSandboxFailure, RunSandboxInstance, RunSandboxKind, RunSandboxPlan, RunSandboxRuntime,
|
||||
RunServerProvenance, RunSessionMetadata, RunSize, RunTarget, SandboxDetails, SandboxInfo,
|
||||
SandboxListMeta, SandboxListResponse, SandboxProviderKind, SandboxProviderLookupError,
|
||||
SandboxService, SandboxServiceListResponse, SecretMetadata, SecretType, ServerSettings,
|
||||
SessionDetail, SessionId, SessionStatus, SessionSummary, SessionTurn,
|
||||
SkillActivationSource, SkillSummary, SkillsProjection, StageCompletion, StageContextWindow,
|
||||
StageContextWindowUnavailableReason, StageHandler, StageId, StageInferenceProjection,
|
||||
StageModelUsage, StageOutcome, StageProjection, StageState, StageToolBatchProjection,
|
||||
SubAgentProjection, SubAgentStatus, SystemActorKind, SystemIntegrationStatus,
|
||||
SystemIntegrationsResponse, TodoListProjection, ToolCategory, ToolSource, ToolSummary,
|
||||
TurnId, UpdateVariableRequest, UserPrincipal, Variable, VariableListResponse, WorkflowPath,
|
||||
WorkflowSettings, WorkflowVersion, WorkflowVersionId,
|
||||
AgentToolsAvailableProps, AskFabro, AuthMethod, AutomationRef, BilledModelUsage,
|
||||
BilledTokenCounts, BlobHash, CommandTermination, Conclusion, ContextWindowBreakdownItem,
|
||||
ContextWindowCategory, ContextWindowCountMethod, ContextWindowSnapshot,
|
||||
ContextWindowStaleness, ContextWindowWarning, CreateVariableRequest, DiffStats,
|
||||
DiffSummary, DirtyStatus, EventEnvelope, ExecOutputTail, FailureCategory, FailureDetail,
|
||||
FailureSignature, GitContext, GitRunTarget, GitRunTarget as AutomationGitWorkflowSource,
|
||||
IdpIdentity, IntegrationConnectionKind, IntegrationConnectionState,
|
||||
IntegrationConnectionStatus, IntegrationProvider, IntegrationStatus, InterviewOption,
|
||||
InterviewQuestionRecord, LlmOutputKind, McpServerDraft as CreateMcpServerRequest,
|
||||
McpServerProjection, McpServerReplace as ReplaceMcpServerRequest, McpServerStatus,
|
||||
McpServerView as McpServer, McpTransportView, Model, ModelControls, ModelCosts,
|
||||
ModelFeatures, ModelLimits, ModelRef as BillingModelRef, ModelTestMode, PairId,
|
||||
PairMessageId, PairMessageRecord, PairMessageRequest, PairRecord, PairStartRequest,
|
||||
PairStatus, PairTarget, PairTranscriptEntry, PairTranscriptResponse, ParallelBranchId,
|
||||
ParallelBranchResult, PendingInterviewRecord, PermissionLevel, Principal, Provider,
|
||||
PullRequest, PullRequestCreation, PullRequestCreationId, PullRequestCreationStatus,
|
||||
PullRequestDetails, PullRequestDetailsStatus, PullRequestDetailsUnavailableReason,
|
||||
PullRequestLink, PullRequestMeta, PullRequestResponse, QuestionType, RepositoryRef,
|
||||
ReviewTarget, ReviewTargetKind, Run, RunApproval, RunApprovalState, RunClientProvenance,
|
||||
RunEvent, RunEventDetailContentKind, RunEventDetailResponse, RunFailure, RunIntent,
|
||||
RunIntentArgs, RunPairStatusResponse, RunProjection, RunProvenance, RunRunnableSource,
|
||||
RunSandbox, RunSandboxFailure, RunSandboxInstance, RunSandboxKind, RunSandboxPlan,
|
||||
RunSandboxRuntime, RunServerProvenance, RunSessionMetadata, RunSize, RunTarget,
|
||||
SandboxDetails, SandboxInfo, SandboxListMeta, SandboxListResponse, SandboxProviderKind,
|
||||
SandboxProviderLookupError, SandboxService, SandboxServiceListResponse, SecretMetadata,
|
||||
SecretType, ServerSettings, SessionDetail, SessionId, SessionStatus, SessionSummary,
|
||||
SessionTurn, SkillActivationSource, SkillSummary, SkillsProjection, StageCompletion,
|
||||
StageContextWindow, StageContextWindowUnavailableReason, StageHandler, StageId,
|
||||
StageInferenceProjection, StageModelUsage, StageOutcome, StageProjection, StageState,
|
||||
StageToolBatchProjection, SubAgentProjection, SubAgentStatus, SystemActorKind,
|
||||
SystemIntegrationStatus, SystemIntegrationsResponse, TodoListProjection, ToolCategory,
|
||||
ToolSource, ToolSummary, TurnId, UpdateVariableRequest, UserPrincipal, Variable,
|
||||
VariableListResponse, WorkflowPath, WorkflowSettings, WorkflowVersion, WorkflowVersionId,
|
||||
};
|
||||
pub use lithos_llm::catalog::{ModelHandle, ProviderId};
|
||||
pub use lithos_llm::types::{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use fabro_api::types::{
|
|||
ActivatedSkill as ApiActivatedSkill, AgentControlState as ApiAgentControlState,
|
||||
AgentMcpToolSummary as ApiAgentMcpToolSummary,
|
||||
AgentToolsAvailableProps as ApiAgentToolsAvailableProps,
|
||||
BilledModelUsage as ApiBilledModelUsage,
|
||||
ContextWindowBreakdownItem as ApiContextWindowBreakdownItem,
|
||||
ContextWindowCategory as ApiContextWindowCategory,
|
||||
ContextWindowCountMethod as ApiContextWindowCountMethod,
|
||||
|
|
@ -23,19 +24,91 @@ use fabro_api::types::{
|
|||
};
|
||||
use fabro_types::{
|
||||
ActivatedSkill, AgentControlState, AgentMcpToolSummary, AgentToolsAvailableProps,
|
||||
ContextWindowBreakdownItem, ContextWindowCategory, ContextWindowCountMethod,
|
||||
BilledModelUsage, ContextWindowBreakdownItem, ContextWindowCategory, ContextWindowCountMethod,
|
||||
ContextWindowSnapshot, ContextWindowStaleness, ContextWindowWarning, LlmOutputKind,
|
||||
McpServerProjection, McpServerStatus, ParallelBranchId, ParallelBranchResult, PermissionLevel,
|
||||
SkillActivationSource, SkillSummary, SkillsProjection, StageContextWindow,
|
||||
McpServerProjection, McpServerStatus, ModelRef, ParallelBranchId, ParallelBranchResult,
|
||||
PermissionLevel, SkillActivationSource, SkillSummary, SkillsProjection, StageContextWindow,
|
||||
StageContextWindowUnavailableReason, StageId, StageInferenceProjection, StageProjection,
|
||||
StageToolBatchProjection, SubAgentProjection, SubAgentStatus, TodoListKind, TodoListProjection,
|
||||
ToolCategory, ToolSource, ToolSummary,
|
||||
};
|
||||
use lithos_llm::catalog::{ModelId, ProviderId};
|
||||
use lithos_llm::types::TokenCounts;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn stage_projection_reuses_canonical_type() {
|
||||
assert_same_type::<ApiStageProjection, StageProjection>();
|
||||
assert_same_type::<ApiBilledModelUsage, BilledModelUsage>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn billing_by_model_rows_match_openapi_json_shape() {
|
||||
let row = BilledModelUsage {
|
||||
model: ModelRef::new(ProviderId::new("openai"), ModelId::new("gpt-5.4")),
|
||||
tokens: TokenCounts {
|
||||
input: 107,
|
||||
output: 51,
|
||||
..TokenCounts::default()
|
||||
},
|
||||
total_usd_micros: Some(321),
|
||||
};
|
||||
let value = serde_json::to_value(&row).unwrap();
|
||||
assert_eq!(
|
||||
value,
|
||||
json!({
|
||||
"model": { "provider": "openai", "model_id": "gpt-5.4" },
|
||||
"tokens": {
|
||||
"input": 107,
|
||||
"output": 51,
|
||||
"reasoning": 0,
|
||||
"cache_read": 0,
|
||||
"cache_write": 0
|
||||
},
|
||||
"total_usd_micros": 321
|
||||
})
|
||||
);
|
||||
let api_row: ApiBilledModelUsage = serde_json::from_value(value).unwrap();
|
||||
assert_eq!(api_row, row);
|
||||
|
||||
let mut stage = StageProjection::new(std::num::NonZeroU32::new(1).unwrap());
|
||||
stage.billing_by_model = vec![row.clone()];
|
||||
let stage_json = serde_json::to_value(&stage).unwrap();
|
||||
assert_eq!(
|
||||
stage_json["billing_by_model"],
|
||||
json!([serde_json::to_value(&row).unwrap()])
|
||||
);
|
||||
let without: StageProjection = serde_json::from_value(json!({
|
||||
"first_event_seq": 1,
|
||||
"prompt": null,
|
||||
"response": null,
|
||||
"completion": null,
|
||||
"provider_used": null,
|
||||
"diff": null,
|
||||
"script_invocation": null,
|
||||
"script_timing": null,
|
||||
"parallel_results": null,
|
||||
"output": null,
|
||||
"usage": {
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"total_tokens": 0,
|
||||
"reasoning_tokens": 0,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_write_tokens": 0
|
||||
},
|
||||
"agent_control": "running",
|
||||
"state": "running"
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(without.billing_by_model.is_empty());
|
||||
assert!(
|
||||
serde_json::to_value(&without)
|
||||
.unwrap()
|
||||
.get("billing_by_model")
|
||||
.is_none(),
|
||||
"no rows, nothing on the wire"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ models/batch-run-lifecycle-request.ts
|
|||
models/batch-run-lifecycle-response.ts
|
||||
models/batch-run-lifecycle-result.ts
|
||||
models/batch-run-lifecycle-summary.ts
|
||||
models/billed-model-usage.ts
|
||||
models/billed-token-counts.ts
|
||||
models/billing-by-model.ts
|
||||
models/billing-model-ref.ts
|
||||
|
|
|
|||
33
lib/packages/fabro-api-client/src/models/billed-model-usage.ts
generated
Normal file
33
lib/packages/fabro-api-client/src/models/billed-model-usage.ts
generated
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.2.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { BillingModelRef } from './billing-model-ref';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { CompletionUsage } from './completion-usage';
|
||||
|
||||
/**
|
||||
* Usage and cost billed to one model: one response, or one model\'s share of a stage.
|
||||
*/
|
||||
export interface BilledModelUsage {
|
||||
'model': BillingModelRef;
|
||||
'tokens': CompletionUsage;
|
||||
/**
|
||||
* Cost for `tokens`, when the provider reported one or the catalog could price them. Absent means no cost data, not zero.
|
||||
*/
|
||||
'total_usd_micros'?: number;
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ export * from './batch-run-lifecycle-request';
|
|||
export * from './batch-run-lifecycle-response';
|
||||
export * from './batch-run-lifecycle-result';
|
||||
export * from './batch-run-lifecycle-summary';
|
||||
export * from './billed-model-usage';
|
||||
export * from './billed-token-counts';
|
||||
export * from './billing-by-model';
|
||||
export * from './billing-model-ref';
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ import type { AgentControlState } from './agent-control-state';
|
|||
import type { AgentSessionProjection } from './agent-session-projection';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { BilledModelUsage } from './billed-model-usage';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { BilledTokenCounts } from './billed-token-counts';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
|
|
@ -145,6 +148,10 @@ export interface StageProjection {
|
|||
* Whether the agent is executing normally or waiting for steering after an interrupt.
|
||||
*/
|
||||
'agent_control': AgentControlState;
|
||||
/**
|
||||
* The completed stage\'s `usage` split by model, as `stage.completed` reported it: the root session\'s route and each subagent\'s own model, a subagent whose model the catalog does not know billed at the root\'s. Sums to `usage`. Empty while the stage runs and for stages without a coding agent; the billing rollup then bills `usage` to `model`.
|
||||
*/
|
||||
'billing_by_model'?: Array<BilledModelUsage>;
|
||||
'agent'?: AgentSessionProjection | null;
|
||||
/**
|
||||
* Lifecycle state of the stage projection.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue