Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ requires-python = ">=3.10"
dependencies = [
# Build-time codegen: parses WIT and emits Python bindings.
"componentize-py>=0.23.0,<1.0.0",
# Forked wasmtime-py with async component model support.
Comment thread
awsarron marked this conversation as resolved.
# Used by ``strandly generate`` (wasmtime.component.bindgen) and at runtime by strands-py-wasm.
"pgrayy-wasmtime @ git+https://github.com/pgrayy/wasm-deps.git@4b5dc41512109ebafe4c4f1edd592c739872c640#subdirectory=wasmtime-py",
# Linter/formatter used by ``strandly check --py`` and ``strandly fmt``.
"ruff>=0.13.0,<0.15.0",
# Type checker used by ``strandly check --py``.
Expand Down
7 changes: 6 additions & 1 deletion strandly/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ program
linkCli()
generate()
build()
installPyWasm()
test()
})

Expand Down Expand Up @@ -183,10 +184,13 @@ function setup(opts?: { node?: boolean; python?: boolean }): void {
if (all || opts?.python) {
run('python3 -m venv .venv', { cwd: ROOT })
run(`${VENV}/bin/pip install -e .`, { cwd: ROOT })
run(`${VENV}/bin/pip install -e strands-py-wasm/`, { cwd: ROOT })
}
}

function installPyWasm(): void {
run(`${VENV}/bin/pip install -e strands-py-wasm/`, { cwd: ROOT })
}

function linkCli(): void {
run('npm link -w strandly')
}
Expand Down Expand Up @@ -250,6 +254,7 @@ function generate(opts?: { check?: boolean }): void {
// Output is a package: one module per WIT interface plus an __init__.py.
// The hand-written ``strands.types`` module re-exports the curated public
// subset from this private ``_generated`` package.
run('rm -rf strands-py-wasm/src/strands/_generated')
run(`${VENV}/bin/python -m wasmtime.component.bindgen wit -o strands-py-wasm/src/strands/_generated`)

// Ensure TS + WASM are built first.
Expand Down
6 changes: 3 additions & 3 deletions strands-py-wasm/tests/test_agent.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from strands import Agent, BedrockModel, StreamEvent, StreamEvent_Stop
from strands import Agent, BedrockModel, types


@pytest.fixture
Expand All @@ -16,6 +16,6 @@ def agent(model):
@pytest.mark.asyncio
async def test_stream_async_hello_world(agent):
async for event in agent.stream_async("Say hello world"):
assert isinstance(event, StreamEvent)
assert isinstance(event, types.StreamEvent)

assert isinstance(event, StreamEvent_Stop)
assert isinstance(event, types.StreamEvent.Stop)
49 changes: 24 additions & 25 deletions strands-wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ In WIT terminology, the WASM component is the "guest" and Python is the "host".

- Node.js 20+
- Python 3.10+
- [wasmtime-py](https://github.com/bytecodealliance/wasmtime-py) (forked build with async component model support)

### First-time setup

Expand All @@ -30,7 +29,7 @@ npm install
npm run dev -- bootstrap
```

`bootstrap` installs toolchains, generates type bindings, builds all layers, and runs all tests. If this command doesn't enable development out of the box, file an issue.
`bootstrap` installs toolchains, links `strandly` to your PATH, generates type bindings, builds all layers, installs `strands-py-wasm`, and runs all tests. If this command doesn't enable development out of the box, file an issue.

## Architecture

Expand All @@ -56,40 +55,39 @@ graph TD
| `strands-ts/` | TypeScript | Agent runtime: event loop, model providers, tools, hooks, streaming |
| `strands-wasm/` | TypeScript | Bridges the TS SDK to WIT exports, compiles to a WASM component |
| `strands-py-wasm/` | Python | Python wrapper: Agent class, @tool decorator, direct WASM host |
| `strands-dev/` | TypeScript | Dev CLI that orchestrates build, test, lint, and CI |
| `dev-docs/` | Markdown | Design proposal and team decisions |
| `strandly/` | TypeScript | Dev CLI that orchestrates build, test, lint, and CI |
| `dev-docs/` | Markdown | Design proposals and team decisions |

### Generated code

`npm run dev -- generate` produces type bindings from `wit/agent.wit` into:
`strandly generate` produces type bindings from `wit/agent.wit` into:

- `strands-ts/generated/`
- `strands-wasm/generated/`
- `strands-ts/generated/` (gitignored)
- `strands-wasm/generated/` (gitignored)
- `strands-py-wasm/src/strands/_generated/` (committed)

Generated files are created by running `npm run dev -- generate` (or `bootstrap`) and are gitignored. Do not edit them by hand. CI runs `generate --check` and fails if they are stale.

Python types are auto-generated into `strands-py-wasm/strands/_generated/types.py` by `strands-py-wasm/scripts/generate_types.py`.
Generated files are created by running `strandly generate` (or `bootstrap`). Do not edit them by hand. CI runs `strandly generate --check` and fails if they are stale.

### Tests

| Layer | Framework | Location |
| -------------- | --------- | ----------------------------------------------------------------- |
| TypeScript SDK | vitest | `strands-ts/src/**/__tests__/` (unit), `strands-ts/test/` (integ) |
| Python wrapper | pytest | `strands-py-wasm/tests_integ/` |
| Python wrapper | pytest | `strands-py-wasm/tests/` |

Add tests alongside the code you change. Bug fixes should include a test that reproduces the original issue.

## Making changes

Each layer depends on the layers above it in the pipeline. The `validate` command rebuilds and tests exactly the layers your change affects.

| What you changed | Validate command |
| ------------------------------------- | ------------------------------------- |
| WIT contract (`wit/agent.wit`) | `npm run dev -- validate wit` |
| TS SDK internals | `npm run dev -- validate ts` |
| TS SDK public API | `npm run dev -- validate ts-api` |
| WASM bridge (`strands-wasm/entry.ts`) | `npm run dev -- validate wasm` |
| Pure Python (`strands-py-wasm/`) | `npm run dev -- validate py` |
| What you changed | Validate command |
| ------------------------------------- | ----------------------------- |
| WIT contract (`wit/agent.wit`) | `strandly validate wit` |
| TS SDK internals | `strandly validate ts` |
| TS SDK public API | `strandly validate ts-api` |
| WASM bridge (`strands-wasm/entry.ts`) | `strandly validate wasm` |
| Pure Python (`strands-py-wasm/`) | `strandly validate py` |

**TS internals vs. public API:** The WASM bridge (`strands-wasm/entry.ts`) imports specific types and functions from `strands-ts/`. If your change modifies something the bridge imports, it is a public API change — use `validate ts-api`. If the bridge does not import it, use `validate ts`.

Expand All @@ -98,19 +96,20 @@ Each layer depends on the layers above it in the pipeline. The `validate` comman
## Dev CLI

```bash
npm run dev -- <command> [options]
strandly <command> [options]
```

Most commands accept layer flags (`--ts`, `--wasm`, `--py`). No flags means all layers.

| Command | What it does |
| ------------------ | ---------------------------------------------------------------------- |
| `bootstrap` | First-time setup: install, generate, build, test |
| `bootstrap` | First-time setup: install, link, generate, build, install py-wasm, test |
| `setup` | Install toolchains (`--node`, `--python`) |
| `link` | Install `strandly` on PATH as a live symlink to this repo |
| `generate` | Regenerate type bindings from WIT (`--check`) |
| `build` | Compile layers (`--ts`, `--wasm`, `--py`, `--release`) |
| `build` | Compile layers (`--ts`, `--wasm`, `--py`) |
| `test` | Run tests (`--py`, `--ts`, or a specific `[file]`) |
| `check` | Lint and type-check (`--ts`, `--py`) |
| `check` | Lint and type-check (`--ts`, `--wasm`, `--py`) |
| `fmt` | Format all code (`--check` to verify without writing) |
| `validate <layer>` | Rebuild and test the layers affected by a change |
| `ci` | Full pipeline: generate, format, lint, build, test |
Expand All @@ -126,14 +125,14 @@ Most commands accept layer flags (`--ts`, `--wasm`, `--py`). No flags means all
| Python | `ruff format` | `ruff check` |

```bash
npm run dev -- fmt # format everything
npm run dev -- check # lint everything
strandly fmt # format everything
strandly check # lint everything
```

Comments are normative statements that describe what code does or why a decision was made. Avoid TODO's without associated issues, notes-to-self, and parenthetical asides.

## Submitting a PR

- Run `npm run dev -- ci` before pushing. This is the same pipeline CI runs.
- Run `strandly ci` before pushing. This is the same pipeline CI runs.
- Keep PRs focused on a single change.
- Use conventional commit messages: `feat:`, `fix:`, `refactor:`, `docs:`, etc.
Loading