Skip to content

Commit bcdf2f6

Browse files
authored
SDK, skill and scenario fixes (#3263)
* SDK, skill and scenario fixes * Scenario correction * Format * Retry policy builder in SDKs * New skills
1 parent 7483d17 commit bcdf2f6

69 files changed

Lines changed: 7367 additions & 1011 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/golem-scala-code-generation/SKILL.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,33 @@ Scans sources for `@agentImplementation` classes using scalameta's parser, then
135135
- `sdks/scala/sbt/src/main/scala/golem/sbt/GolemPlugin.scala` — sbt wrapper (source generator + module initializer)
136136
- `sdks/scala/mill/src/golem/mill/GolemAutoRegister.scala` — Mill wrapper
137137

138-
### 2. Scala 3 Macros (compile-time, not build-time)
138+
### 2. RPC Client Generation (shared codegen + sbt/Mill wrappers)
139+
140+
Scans sources for `@agentDefinition` traits, extracts their method surfaces, and generates `XClient` companion objects with `XRemote` traits and per-method wrapper classes.
141+
142+
**Generated per-method class provides five call modes:**
143+
- `apply(args...)` — async await via `asyncInvokeAndAwait` host function + pollable
144+
- `cancelable(args...)` — returns `(Future[Out], CancellationToken)` for cancellable async await
145+
- `trigger(args...)` — fire-and-forget via `invoke` host function
146+
- `scheduleAt(args..., when)` — scheduled invocation
147+
- `scheduleCancelableAt(args..., when)` — cancelable scheduled invocation
148+
149+
**Runtime call chain:** Generated method → `AbstractRemoteMethod.awaitWith/cancelableAwaitWith/triggerWith/scheduleWith``ResolvedAgent.await/cancelableAwait/trigger/schedule``RpcInvoker.asyncInvokeAndAwait/cancelableAsyncInvokeAndAwait/invoke/...``WasmRpcApi.WasmRpcClient` → WIT `golem:agent/host@1.5.0` `wasm-rpc` resource
150+
151+
**Key async detail:** The default `apply()` path uses `async-invoke-and-await` (not `invoke-and-await`), returning a `FutureInvokeResult` resource. The runtime polls via `subscribe()``pollable.promise()``get()`, yielding genuine async `Future`s that allow concurrent RPC calls. This matches the TypeScript SDK behavior.
152+
153+
**Files:**
154+
- `sdks/scala/codegen/src/main/scala/golem/codegen/rpc/RpcCodegen.scala` — shared generation logic
155+
- `sdks/scala/core/js/src/main/scala/golem/runtime/rpc/AbstractRemoteMethod.scala` — base class for generated wrappers
156+
- `sdks/scala/core/js/src/main/scala/golem/runtime/rpc/AgentClientRuntime.scala``ResolvedAgent` with async/cancelable dispatch
157+
- `sdks/scala/core/js/src/main/scala/golem/runtime/rpc/RemoteAgentClient.scala``WasmRpcInvoker` implementing pollable-based async
158+
- `sdks/scala/core/js/src/main/scala/golem/runtime/rpc/host/WasmRpcApi.scala` — Scala.js facades for `WasmRpc` and `FutureInvokeResult`
159+
- `sdks/scala/core/js/src/main/scala/golem/runtime/rpc/RpcInvoker.scala` — trait with sync, async, and cancelable invoke methods
160+
- `sdks/scala/core/js/src/main/scala/golem/runtime/rpc/CancellationToken.scala` — cancellation token (wraps `() => Unit`)
161+
- `sdks/scala/sbt/src/main/scala/golem/sbt/GolemPlugin.scala` — sbt wrapper
162+
- `sdks/scala/mill/src/golem/mill/GolemAutoRegister.scala` — Mill wrapper
163+
164+
### 3. Scala 3 Macros (compile-time, not build-time)
139165

140166
Macros generate code at compile time, not as a build step. They live in `sdks/scala/macros/` and use `scala.quoted.*`:
141167

.agents/skills/golem-scala-development/SKILL.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,39 @@ This copies all WIT packages from `wit/deps/` into `sdks/scala/wit/deps/`. The r
222222
### TypeScript SDK Reference
223223
The TypeScript SDK at `sdks/ts/wit/` is the reference for correct WIT definitions when in doubt.
224224

225+
## RPC Client Architecture
226+
227+
The Scala SDK's remote agent call path uses `async-invoke-and-await` from the WIT `golem:agent/host@1.5.0` interface, matching the TypeScript SDK behavior:
228+
229+
### Host functions used
230+
231+
| WIT function | Scala SDK usage |
232+
|---|---|
233+
| `wasm-rpc.async-invoke-and-await` | Default `apply()` and `cancelable()` — returns `FutureInvokeResult`, polled via `subscribe()``pollable.promise()``get()` |
234+
| `wasm-rpc.invoke` | Fire-and-forget `trigger()` |
235+
| `wasm-rpc.invoke-and-await` | Kept for backward compatibility but not used by generated clients |
236+
| `wasm-rpc.schedule-invocation` | `scheduleAt()` |
237+
| `wasm-rpc.schedule-cancelable-invocation` | `scheduleCancelableAt()` |
238+
239+
### Key files
240+
241+
| File | Role |
242+
|---|---|
243+
| `core/js/.../host/WasmRpcApi.scala` | Scala.js `@JSImport` facades for `WasmRpc`, `FutureInvokeResult` (with `subscribe`/`get`/`cancel`) |
244+
| `core/js/.../rpc/RpcInvoker.scala` | Trait defining `invokeAndAwait`, `asyncInvokeAndAwait`, `cancelableAsyncInvokeAndAwait`, `invoke`, `schedule*` |
245+
| `core/js/.../rpc/RemoteAgentClient.scala` | `WasmRpcInvoker` — implements async polling via `pollable.promise()``FutureInterop.fromPromise` |
246+
| `core/js/.../rpc/AgentClientRuntime.scala` | `ResolvedAgent``runAwaitable` uses `asyncInvokeAndAwait`; `runCancelableAwaitable` returns `(Future, CancellationToken)` |
247+
| `core/js/.../rpc/AbstractRemoteMethod.scala` | Base class for generated per-method wrappers (`awaitWith`, `cancelableAwaitWith`, `triggerWith`, `scheduleWith`) |
248+
| `core/js/.../rpc/CancellationToken.scala` | Wraps a `() => Unit` cancel function (from `FutureInvokeResult.cancel()` or `RawCancellationToken`) |
249+
| `codegen/.../rpc/RpcCodegen.scala` | Generates `XClient` objects with `apply`, `cancelable`, `trigger`, `scheduleAt`, `scheduleCancelableAt` |
250+
251+
### Async behavior
252+
253+
- `apply()` returns a genuinely async `Future[Out]` — the WASM event loop is yielded while waiting
254+
- `cancelable()` returns `(Future[Out], CancellationToken)` — calling `token.cancel()` invokes `FutureInvokeResult.cancel()` (best-effort)
255+
- Multiple concurrent RPC calls are possible since each uses its own `FutureInvokeResult` resource
256+
- `trigger()`, `scheduleAt()`, `scheduleCancelableAt()` remain synchronous (wrapped in `Future`)
257+
225258
## Known Issue: Multi-Component App Scala.js Linking Error
226259

227260
When a Scala component is part of a **multi-component** (mixed-language) app, the `build_mixed_language_app` CLI test fails with:

cli/golem-cli/templates/moonbit/common/AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ This project includes coding-agent skills in `.agents/skills/`. Load a skill whe
4949
| `golem-manage-plugins` | Managing Golem plugins — listing available plugins, installing and configuring plugins via golem.yaml or CLI, and understanding built-in plugins like the OTLP exporter |
5050
| `golem-add-config-moonbit` | Adding typed configuration to a MoonBit Golem agent |
5151
| `golem-add-secret-moonbit` | Adding secrets to MoonBit Golem agents |
52+
| `golem-quota-moonbit` | Adding resource quotas (rate limiting, capacity, concurrency) to MoonBit Golem agents using QuotaToken and reservations |
53+
| `golem-retry-policies-moonbit` | Configuring semantic retry policies — composable exponential/periodic/fibonacci backoff, predicates on error properties, scoped overrides with `with_named_policy!`, and live CLI management |
5254
| `golem-profiles-and-environments` | Understanding CLI profiles, app environments, and component presets — switching between local/cloud, managing deployment targets, and activating per-environment configuration |
5355
| `golem-add-env-vars` | Defining environment variables for agents in golem.yaml and via CLI |
5456
| `golem-add-initial-files` | Adding initial files to agent filesystems via golem.yaml |

cli/golem-cli/templates/rust/common/AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ This project includes coding-agent skills in `.agents/skills/`. Load a skill whe
4949
| `golem-manage-plugins` | Managing Golem plugins — listing available plugins, installing and configuring plugins via golem.yaml or CLI, and understanding built-in plugins like the OTLP exporter |
5050
| `golem-add-config-rust` | Adding typed configuration to a Rust Golem agent |
5151
| `golem-add-secret-rust` | Adding secrets to Rust Golem agents |
52+
| `golem-quota-rust` | Adding resource quotas (rate limiting, capacity, concurrency) to Rust Golem agents using QuotaToken and reservations |
53+
| `golem-retry-policies-rust` | Configuring semantic retry policies — composable exponential/periodic/fibonacci backoff, predicates on error properties, scoped overrides with `with_named_policy`, and live CLI management |
5254
| `golem-profiles-and-environments` | Understanding CLI profiles, app environments, and component presets — switching between local/cloud, managing deployment targets, and activating per-environment configuration |
5355
| `golem-add-env-vars` | Defining environment variables for agents in golem.yaml and via CLI |
5456
| `golem-add-initial-files` | Adding initial files to agent filesystems via golem.yaml |

cli/golem-cli/templates/scala/common/AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ This project includes coding-agent skills in `.agents/skills/`. Load a skill whe
4949
| `golem-manage-plugins` | Managing Golem plugins — listing available plugins, installing and configuring plugins via golem.yaml or CLI, and understanding built-in plugins like the OTLP exporter |
5050
| `golem-add-config-scala` | Adding typed configuration to Scala Golem agents |
5151
| `golem-add-secret-scala` | Adding secrets to Scala Golem agents |
52+
| `golem-quota-scala` | Adding resource quotas (rate limiting, capacity, concurrency) to Scala Golem agents using QuotaToken and reservations |
53+
| `golem-retry-policies-scala` | Configuring semantic retry policies — composable exponential/periodic/fibonacci backoff, predicates on error properties, scoped overrides with `withRetryPolicy`, and live CLI management |
5254
| `golem-profiles-and-environments` | Understanding CLI profiles, app environments, and component presets — switching between local/cloud, managing deployment targets, and activating per-environment configuration |
5355
| `golem-add-env-vars` | Defining environment variables for agents in golem.yaml and via CLI |
5456
| `golem-add-initial-files` | Adding initial files to agent filesystems via golem.yaml |

cli/golem-cli/templates/ts/common/AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ This project includes coding-agent skills in `.agents/skills/`. Load a skill whe
4949
| `golem-manage-plugins` | Managing Golem plugins — listing available plugins, installing and configuring plugins via golem.yaml or CLI, and understanding built-in plugins like the OTLP exporter |
5050
| `golem-add-config-ts` | Adding typed configuration to a TypeScript Golem agent |
5151
| `golem-add-secret-ts` | Adding secrets to TypeScript Golem agents |
52+
| `golem-quota-ts` | Adding resource quotas (rate limiting, capacity, concurrency) to TypeScript Golem agents using QuotaToken and reservations |
53+
| `golem-retry-policies-ts` | Configuring semantic retry policies — composable exponential/periodic/fibonacci backoff, predicates on error properties, scoped overrides with `withRetryPolicy`, and live CLI management |
5254
| `golem-profiles-and-environments` | Understanding CLI profiles, app environments, and component presets — switching between local/cloud, managing deployment targets, and activating per-environment configuration |
5355
| `golem-add-env-vars` | Defining environment variables for agents in golem.yaml and via CLI |
5456
| `golem-add-initial-files` | Adding initial files to agent filesystems via golem.yaml |
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
name: golem-quota-moonbit
3+
description: "Adding resource quotas to a MoonBit Golem agent. Use when the user asks about rate limiting, resource quotas, quota tokens, QuotaToken, with_reservation, throttling API calls, limiting concurrency, capacity limits, or splitting tokens between agents."
4+
---
5+
6+
# Adding Resource Quotas to an Agent (MoonBit)
7+
8+
Golem provides a distributed resource quota system via the `@quota` module. Quotas let you define limited resources (API call rates, storage capacity, connection concurrency) and enforce consumption limits across all agents in a deployment.
9+
10+
## 1. Define Resources in the Application Manifest
11+
12+
Add resource definitions under `resourceDefaults` in `golem.yaml`, scoped per environment:
13+
14+
```yaml
15+
resourceDefaults:
16+
prod:
17+
api-calls:
18+
limit:
19+
type: Rate
20+
value: 100
21+
period: minute
22+
max: 1000
23+
enforcementAction: reject
24+
unit: request
25+
units: requests
26+
storage:
27+
limit:
28+
type: Capacity
29+
value: 1073741824 # 1 GB
30+
enforcementAction: reject
31+
unit: byte
32+
units: bytes
33+
connections:
34+
limit:
35+
type: Concurrency
36+
value: 50
37+
enforcementAction: throttle
38+
unit: connection
39+
units: connections
40+
```
41+
42+
### Limit Types
43+
44+
- **`Rate`** — refills `value` tokens every `period` (second/minute/hour/day), capped at `max`. Use for rate-limiting API calls.
45+
- **`Capacity`** — fixed pool of `value` tokens. Once consumed, never refilled. Use for storage budgets.
46+
- **`Concurrency`** — pool of `value` tokens returned when released. Use for limiting parallel connections.
47+
48+
### Enforcement Actions
49+
50+
- **`reject`** — returns `Err(FailedReservation)`. The agent must handle the error.
51+
- **`throttle`** — Golem suspends the agent until capacity is available. Fully automatic, no code needed.
52+
- **`terminate`** — kills the agent with a failure message.
53+
54+
## 2. Acquire a QuotaToken
55+
56+
Acquire a `QuotaToken` once per resource, typically in the agent constructor:
57+
58+
```moonbit
59+
let token = @quota.QuotaToken::new("api-calls", 1UL)
60+
```
61+
62+
The second parameter is the **expected amount per reservation** (`UInt64`), used for fair scheduling. For simple 1-call = 1-token rate limiting, use `1UL`.
63+
64+
## 3. Simple Rate Limiting with `with_reservation`
65+
66+
Use `@quota.with_reservation` to reserve tokens, run code, and commit actual usage:
67+
68+
```moonbit
69+
let result = @quota.with_reservation(token, 1UL, fn(reservation) {
70+
let response = call_simple_api()
71+
(1UL, response)
72+
})
73+
```
74+
75+
The callback returns `(UInt64, T)` where the first element is actual usage. If actual < reserved, unused capacity returns to the pool.
76+
77+
## 4. Variable-Cost Reservations (e.g., LLM Tokens)
78+
79+
Reserve the maximum expected cost, then commit actual usage:
80+
81+
```moonbit
82+
let result = @quota.with_reservation(token, 4000UL, fn(reservation) {
83+
let response = call_llm(prompt, max_tokens=4000)
84+
(response.tokens_used, response)
85+
})
86+
```
87+
88+
## 5. Manual Reserve / Commit
89+
90+
For finer control, use `reserve` and `commit` directly:
91+
92+
```moonbit
93+
match token.reserve(100UL) {
94+
Ok(reservation) => {
95+
let result = do_work()
96+
reservation.commit(result.actual_usage)
97+
}
98+
Err(failed) => @log.warn("Quota unavailable")
99+
}
100+
```
101+
102+
## 6. Splitting Tokens for Agent-to-Agent RPC
103+
104+
Split a portion of your quota to pass to a child agent:
105+
106+
```moonbit
107+
let child_token = self.token.split(200UL)
108+
let child_agent = SummarizerAgent::new_phantom()
109+
child_agent.summarize(text, child_token)
110+
```
111+
112+
The child agent receives the `QuotaToken` as a method parameter and uses it for its own reservations. Merge returned tokens back:
113+
114+
```moonbit
115+
token.merge(returned_token)
116+
```
117+
118+
## 7. Dynamic Resource Updates via CLI
119+
120+
Modify resource limits at runtime — changes affect running agents immediately:
121+
122+
```shell
123+
golem resource update api-calls --limit '{"type":"rate","value":200,"period":"minute","max":2000}' --environment prod
124+
```
125+
126+
## Key Constraints
127+
128+
- Acquire `QuotaToken` once and reuse — do not create a new one per call
129+
- All quota amounts are `UInt64` values (use `1UL`, `200UL`, etc.)
130+
- `split` traps if `child_expected_use` exceeds the parent's current expected-use
131+
- `merge` traps if the tokens refer to different resources
132+
- `with_reservation` returns `Result[T, FailedReservation]` — `Err` only for `reject` enforcement; `throttle` suspends transparently
133+
- Resource names in code must match the names in `golem.yaml` `resourceDefaults`

0 commit comments

Comments
 (0)