Skip to content
Merged
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
38 changes: 37 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,28 @@ The system consists of three major components:
- **Server**: HTTP API backend that collects, stores, indexes, and serves observations
- **Web UI**: Interface for searching and visualizing observations (served from server)

For more details refer to [Design](docs/design.md)
## Architecture

Cargo workspace with 4 crates:

| Crate | Type | Purpose |
|-------|------|---------|
| `observation-tools-client` | lib (cdylib + rlib) | Rust client + Node.js native module (NAPI) |
| `observation-tools-server` | bin + lib | HTTP API, web UI, storage (sled + object_store) |
| `observation-tools-shared` | lib | Core types shared across crates (WASM-compatible) |
| `observation-tools-macros` | proc-macro | `observe!()` and `group!()` macros |

Client has optional feature flags: `axum` (middleware), `tracing` (subscriber integration).

## Commands

```bash
cargo build --workspace --all-features # Build
cargo run --bin observation-tools -- serve # Run server (default port 3000)
cargo test --workspace --all-features # Rust tests
```

For UI tests, formatting, and contribution setup, see [CONTRIBUTING.md](CONTRIBUTING.md).

## Core Concepts

Expand All @@ -27,3 +48,18 @@ For more details refer to [Design](docs/design.md)
- Always use workspace imports for dependencies to ensure consistency across crates.
- Never use `unwrap()`. Always handle or propagate errors.
- Only comment code when necessary. Prefer self-documenting code.

## Environment

| Variable | Purpose |
|----------|---------|
| `PORT` | Server listen port (default: 3000) |
| `SERVER_URL` | Point integration tests at an external server instead of spawning one |
| `RUST_LOG` | Logging filter (default set in `.cargo/config.toml`) |

## Testing

- Rust integration tests are in `crates/observation-tools-client/tests/` with a shared `TestServer` helper
that auto-spawns a server on a random port (or uses `SERVER_URL` for an external server).
- Playwright E2E tests are in `tests/` and test the web UI in Chromium.
- Always use `--all-features` when running `cargo test` to cover axum/tracing integrations.
42 changes: 42 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Contribution guidelines

## Running tests

We have two test suites: rust-based client+server integration tests and a playwright based UI test suite. By default,
every test will start up its own server instance.

- You can use `SERVER_URL` to point the tests to a running server instead of starting a new one,
though keep in mind many tests assume a clean server state.

### Rust tests

```bash
cargo test --workspace --all-features
```

### UI tests

To run the tests, you must build the NodeJS client library so the test can import it.

```bash
pnpm --dir crates/observation-tools-client install
pnpm --dir crates/observation-tools-client build:debug
pnpm --dir tests install
pnpm --dir tests run test
```

The test suite uses [Playwright](http://playwright.dev/). You can use all of its debugging tools, e.g. open the
inspector:

```bash
cd tests
pnpm playwright test --ui
```

## Formatting

```bash
cargo +nightly fmt
pnpm dlx prettier --write .
```

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"crates/observation-tools-server",
"crates/observation-tools-client",
"crates/observation-tools-macros",
"examples"
]

[workspace.package]
Expand Down
40 changes: 1 addition & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,7 @@ cargo run --bin observation-tools -- serve

## Contributing

### Running tests

We have two test suites: rust-based client+server integration tests and a playwright based UI test suite. By default,
every test will start up its own server instance.

- You can use `SERVER_URL` to point the tests to a running server instead of starting a new one,
though keep in mind many tests assume a clean server state.

#### Rust tests

```bash
cargo test
```

#### UI tests

To run the tests, you must build the NodeJS client library so the test can import it.

```bash
pnpm --dir crates/observation-tools-client install
pnpm --dir crates/observation-tools-client build:debug
pnpm --dir tests install
pnpm --dir tests run test
```

The test suite uses [Playwright](http://playwright.dev/). You can use all of its debugging tools, e.g. open the
inspector:

```bash
cd tests
pnpm playwright test --ui
```

### Formatting

```bash
cargo +nightly fmt
pnpm dlx prettier --write .
```
See the [contributing guidelines](CONTRIBUTING.md).

## License

Expand Down
18 changes: 18 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "examples"
version = "0.0.0"
publish = false
edition = "2021"

# If you copy one of the examples into a new project, you should be using
# [dependencies] instead, and delete the **path**.
[dev-dependencies]
observation-tools = { path = "../crates/observation-tools-client" }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[[example]]
name = "getting-started"
path = "getting-started.rs"
57 changes: 57 additions & 0 deletions examples/getting-started.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use observation_tools::{group, observe, with_execution, ClientBuilder};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1. Create a client pointing at a running observation-tools server
let client = ClientBuilder::new()
.base_url("http://localhost:3000")
.build()?;

// 2. Begin an execution — this is the root scope for all observations
let execution = client
.begin_execution("getting-started")?
.wait_for_upload()
.await?;
println!("Execution URL: {}", execution.url());

// 3. Use with_execution to set the context for observe! calls
with_execution(execution, async {
let user_message = "What is the topic of this document?";
observe!("user_message").payload(user_message);

let document_content = load_document_content().await;
observe!("document_content").serde(&document_content);

let api_request = serde_json::json!({
"message": user_message,
"document": document_content,
});
observe!("api_call").serde(&api_request);
let api_response = call_api(api_request).await;
observe!("api_response").serde(&api_response);

let group = group!("processing_steps").build().into_handle();
observe!("hello").group(&group).payload("Hello, world!");
})
.await;

// 4. Shut down the client to flush all pending uploads
client.shutdown().await?;
println!("Done! Open the execution URL above to view your observations.");

Ok(())
}

async fn load_document_content() -> serde_json::Value {
serde_json::json!({
"title": "Rust Programming Guide",
"content": "Rust offers memory safety and fast performance.",
})
}

async fn call_api(_request: serde_json::Value) -> serde_json::Value {
serde_json::json!({
"message": "The main theme of the document is Rust programming.",
})
}

Loading