Client Configuration Reference for the Cycles Spring Boot Starter
Using Python?
See the Python Client Configuration Reference instead.
This is the complete reference for all configuration properties available in the Cycles Spring Boot Starter.
All properties are under the cycles prefix in your project's application.yml (or application.properties).
Required properties
| Property | Type | Description |
|---|---|---|
cycles.base-url | String | Base URL of the Cycles server (e.g., http://localhost:7878) |
cycles.api-key | String | API key for authentication |
If either is missing or blank, the application will fail to start with a configuration error.
Subject defaults
These properties set default values for the Subject fields used in @Cycles annotations. They apply to all annotated methods unless overridden at the annotation level or by a CyclesFieldResolver.
| Property | Type | Default | Description |
|---|---|---|---|
cycles.tenant | String | (none) | Default tenant |
cycles.workspace | String | (none) | Default workspace |
cycles.app | String | (none) | Default application name |
cycles.workflow | String | (none) | Default workflow |
cycles.agent | String | (none) | Default agent |
cycles.toolset | String | (none) | Default toolset |
Resolution order
For each Subject field, the starter resolves the value using this priority:
- Annotation attribute — if set on the
@Cyclesannotation, it wins - Configuration property — if set in
application.yml - CyclesFieldResolver bean — if a bean named after the field exists (e.g., a bean named
"tenant"implementingCyclesFieldResolver)
If none of these provide a value, the field is omitted from the request.
Per-annotation overrides and budget scope targeting
When you override a subject field on @Cycles, the resolved subject changes, which targets a different budget scope on the server.
Example: Given this configuration:
cycles:
tenant: acme
workspace: production
app: support-botA method with @Cycles(value = "1000", workspace = "staging") resolves to:
| Field | Source | Resolved value |
|---|---|---|
tenant | config | acme |
workspace | annotation | staging |
app | config | support-bot |
The reservation targets scope tenant:acme/workspace:staging/app:support-bot instead of tenant:acme/workspace:production/app:support-bot.
Budget scope implications: Each level in the scope hierarchy has an independent budget. If you have:
tenant:acme/workspace:production→ $10,000 budgettenant:acme/workspace:staging→ $1,000 budget
Using workspace = "staging" on an annotation targets the $1,000 staging budget. Without the override, the same method targets the $10,000 production budget.
WARNING
A reservation must pass budget checks at all affected scope levels. If you set tenant and workspace, the server checks remaining budget at both tenant:acme and tenant:acme/workspace:staging. If either scope is exhausted, the reservation is denied.
@Cycles annotation attributes
The @Cycles annotation controls reservation behavior per-method. These are separate from the application.yml configuration above. For full documentation and examples, see Getting Started with the Spring Boot Starter — Annotation attributes.
| Attribute | Type | Default | Description |
|---|---|---|---|
value | String | "" | SpEL expression for estimated cost. Shorthand: @Cycles("1000"). Synonym for estimate. |
estimate | String | "" | SpEL expression for estimated cost. Synonym for value. |
actual | String | "" | SpEL expression for actual cost, evaluated after method returns. #result is bound to the return value. |
actionKind | String | "" | Action category (e.g. "llm.completion"). Defaults to declaring class simple name if blank. |
actionName | String | "" | Action identifier (e.g. "gpt-4"). Defaults to method name if blank. |
actionTags | String[] | {} | Tags for filtering and reporting (e.g. {"prod", "customer-facing"}). |
unit | String | "USD_MICROCENTS" | Budget unit: USD_MICROCENTS, TOKENS, CREDITS, RISK_POINTS. |
ttlMs | long | 60000 | Reservation TTL in milliseconds (1,000–86,400,000). |
gracePeriodMs | long | -1 | Grace period after TTL expiry in milliseconds. When -1, the server applies its default (5000ms). Valid range: 0–60,000. |
overagePolicy | String | "ALLOW_IF_AVAILABLE" | "REJECT", "ALLOW_IF_AVAILABLE", or "ALLOW_WITH_OVERDRAFT". |
dryRun | boolean | false | Shadow-mode evaluation. If true, server evaluates without persisting; guarded method does NOT execute. |
useEstimateIfActualNotProvided | boolean | true | When true and actual is blank, use the estimate as actual at commit time. |
tenant | String | "" | Subject tenant override (takes precedence over config and resolver). |
workspace | String | "" | Subject workspace override. |
app | String | "" | Subject app override. |
workflow | String | "" | Subject workflow override. |
agent | String | "" | Subject agent override. |
toolset | String | "" | Subject toolset override. |
dimensions | String[] | {} | Custom dimensions as "key=value" pairs (e.g. {"cost_center=engineering"}). |
HTTP configuration
| Property | Type | Default | Description |
|---|---|---|---|
cycles.http.connect-timeout | Duration | 2s | TCP connection timeout to the Cycles server |
cycles.http.read-timeout | Duration | 5s | Read timeout for responses from the Cycles server |
Duration values use Spring Boot duration syntax: 2s, 500ms, 1m, etc.
Example
cycles:
http:
connect-timeout: 3s
read-timeout: 10sFor long-running operations where the server may take longer to respond (e.g., under heavy load), increase the read timeout.
Retry configuration
Controls the commit retry engine for transient failures.
| Property | Type | Default | Description |
|---|---|---|---|
cycles.retry.enabled | boolean | true | Enable automatic commit retries |
cycles.retry.max-attempts | int | 5 | Maximum number of retry attempts |
cycles.retry.initial-delay | Duration | 500ms | Delay before the first retry |
cycles.retry.multiplier | double | 2.0 | Backoff multiplier between retries |
cycles.retry.max-delay | Duration | 30s | Maximum delay between retries |
How retry works
When a commit fails with a transport error or 5xx response, the retry engine schedules a retry using exponential backoff:
Attempt 1: wait 500ms
Attempt 2: wait 1000ms
Attempt 3: wait 2000ms
Attempt 4: wait 4000ms
Attempt 5: wait 8000ms (capped at max-delay)Non-retryable errors (4xx responses) are not retried.
Disabling retry
cycles:
retry:
enabled: falseAggressive retry for critical commits
cycles:
retry:
max-attempts: 10
initial-delay: 200ms
multiplier: 1.5
max-delay: 60sFull configuration example
Add the following to your project's application.yml:
cycles:
# Required
base-url: ${CYCLES_BASE_URL:http://localhost:7878}
api-key: ${CYCLES_API_KEY}
# Subject defaults
tenant: acme
workspace: production
app: support-bot
# HTTP settings
http:
connect-timeout: 2s
read-timeout: 5s
# Commit retry
retry:
enabled: true
max-attempts: 5
initial-delay: 500ms
multiplier: 2.0
max-delay: 30sEquivalent application.properties
Alternatively, add to your project's application.properties:
cycles.base-url=${CYCLES_BASE_URL:http://localhost:7878}
cycles.api-key=${CYCLES_API_KEY}
cycles.tenant=acme
cycles.workspace=production
cycles.app=support-bot
cycles.http.connect-timeout=2s
cycles.http.read-timeout=5s
cycles.retry.enabled=true
cycles.retry.max-attempts=5
cycles.retry.initial-delay=500ms
cycles.retry.multiplier=2.0
cycles.retry.max-delay=30sAuto-configured beans
The starter auto-configures the following beans, all with @ConditionalOnMissingBean so you can override any of them:
| Bean | Type | Purpose |
|---|---|---|
cyclesWebClient | WebClient | HTTP client with configured timeouts |
cyclesClient | CyclesClient | Protocol client (DefaultCyclesClient) |
evaluator | CyclesExpressionEvaluator | SpEL evaluator |
cyclesRequestBuilderService | CyclesRequestBuilderService | Builds protocol request bodies |
cyclesValueResolutionService | CyclesValueResolutionService | Resolves Subject field values |
retryEngine | CommitRetryEngine | Handles commit retries (InMemoryCommitRetryEngine) |
cyclesLifecycleService | CyclesLifecycleService | Orchestrates the full lifecycle |
aspect | CyclesAspect | AOP aspect for @Cycles annotation |
Overriding a bean
To replace any auto-configured bean, define your own:
@Configuration
public class CustomCyclesConfig {
@Bean
public CyclesClient cyclesClient() {
// Your custom implementation
return new MyCustomCyclesClient();
}
}The auto-configuration will skip creating its default CyclesClient when it detects yours.
Environment-specific configuration
Using Spring profiles
Create profile-specific files in src/main/resources/:
# application.yml (shared)
cycles:
tenant: acme
retry:
enabled: true
---
# application-dev.yml
cycles:
base-url: http://localhost:7878
api-key: dev-key
---
# application-prod.yml
cycles:
base-url: https://cycles.internal.example.com
api-key: ${CYCLES_API_KEY}
http:
read-timeout: 10sUsing environment variables
Every property can be set via environment variables using Spring Boot's relaxed binding:
| Property | Environment variable |
|---|---|
cycles.base-url | CYCLES_BASE_URL |
cycles.api-key | CYCLES_API_KEY |
cycles.tenant | CYCLES_TENANT |
cycles.http.connect-timeout | CYCLES_HTTP_CONNECT_TIMEOUT |
cycles.retry.max-attempts | CYCLES_RETRY_MAX_ATTEMPTS |
Next steps
- Getting Started with the Spring Boot Starter — quick start guide
- SpEL Expression Reference — expression syntax
- Custom Field Resolvers — dynamic Subject field resolution
- Server Configuration Reference — server-side properties