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
160 changes: 131 additions & 29 deletions .github/workflows/ci-py311.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,131 @@
name: CASTS Python Tests

on:
pull_request:
paths:
- "geaflow-ai/plugins/casts/**"
push:
branches: ["master"]
paths:
- "geaflow-ai/plugins/casts/**"
workflow_dispatch:

jobs:
tests:
runs-on: ubuntu-latest
defaults:
run:
working-directory: geaflow-ai/plugins/casts
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install uv
run: pip install uv
- name: Sync deps
run: uv sync
- name: Run tests
run: uv run pytest
################################################################################
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
################################################################################

name: Python CI (uv + 3.11)

on:
pull_request:
paths:
- "geaflow-ai/plugins/casts/**"
- "geaflow-ai/plugins/lightmem/**"
- ".github/workflows/ci-py311.yml"
push:
branches: ["master"]
paths:
- "geaflow-ai/plugins/casts/**"
- "geaflow-ai/plugins/lightmem/**"
- ".github/workflows/ci-py311.yml"
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.number || github.run_id }}
cancel-in-progress: true

jobs:
tests:
name: ${{ matrix.plugin }} (py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]
plugin: ["casts", "lightmem"]

defaults:
run:
shell: bash
working-directory: geaflow-ai/plugins/${{ matrix.plugin }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Setup uv
uses: astral-sh/setup-uv@v4
with:
version: "0.9.17"
enable-cache: true

- name: Sync deps
run: |
python -m venv .venv
uv sync --extra dev --no-editable

- name: Lint (ruff)
run: |
uv run --no-editable ruff check .

- name: Format check (ruff)
run: |
uv run --no-editable ruff format --check .

- name: Type check (mypy)
run: |
if [[ "${{ matrix.plugin }}" == "casts" ]]; then
uv run --no-editable mypy -p api -p core -p services -p harness
else
uv run --no-editable mypy -p lightmem -p api
fi

- name: Run tests
run: |
uv run --no-editable pytest -q

smoke:
name: smoke-${{ matrix.plugin }} (py${{ matrix.python-version }})
runs-on: ubuntu-latest
needs: tests
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]
plugin: ["casts", "lightmem"]

defaults:
run:
shell: bash
working-directory: geaflow-ai/plugins/${{ matrix.plugin }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Setup uv
uses: astral-sh/setup-uv@v4
with:
version: "0.9.17"
enable-cache: true

- name: Prepare venv
run: |
python -m venv .venv

- name: Smoke test
run: |
bash ./scripts/smoke.sh
2 changes: 1 addition & 1 deletion geaflow-ai/plugins/casts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ uv.lock
.DS_Store

# Data files
data/real_graph_data/
harness/data/real_graph_data/
casts_traversal_path_req_*.png
130 changes: 112 additions & 18 deletions geaflow-ai/plugins/casts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,41 @@ CASTS stands for **Context-Aware Strategy Cache System**.

- Cache traversal strategies (SKUs) to reduce repeated LLM calls.
- Separate schema metadata from execution logic.
- Support both synthetic and real-world graph data.
- Support both synthetic and real-world graph data (in the Python harness).
- Keep the core cache logic deterministic and testable.

## Two Modes: Production vs. Python Harness

CASTS is designed so that **decisioning** and **execution** are separate concerns.

### Production Mode (GeaFlow Java Integration)

In production, the **GeaFlow Java data plane executes traversal** and CASTS is
used as a *decision service*:

- Python returns a **single next-step Gremlin-style step string** (e.g. `out('friend')`, `inV()`, `stop`).
- Python does **not** execute multi-hop traversal against graph data.
- Java (`CastsOperator`) executes the step, expands the subgraph, and repeats until
`stop` or `maxDepth`.

Key integration files in this repo:

- Java executor/operator: `geaflow-ai/src/main/java/org/apache/geaflow/ai/casts/CastsOperator.java`
- Python decision service: `geaflow-ai/plugins/casts/api/app.py` (`POST /casts/decision`)

### Python Harness Mode (Offline Simulation)

The CASTS repo also includes a **Python-only harness** for experiments:

- It implements its own in-memory data source + traversal executor for evaluation.
- It is intended for offline testing, hit-rate studies, and algorithm iteration.
- It can be misleading if read as “production execution”; treat it as a harness.

## Module Layout

- `core`: cache, schema, configuration, and core models
- `data`: data sources and graph generation (synthetic + real)
- `services`: embedding + LLM services
- `simulation`: simulation engine and evaluation
- `core`: decision core (cache, models, GremlinStateMachine, validation)
- `services`: embedding + LLM integrations used by decisioning
- `harness`: Python-only data + simulation + executor (not production execution)
- `tests`: unit and integration tests

## Repository Placement
Expand All @@ -30,8 +56,9 @@ plugin, with the Python package located at the module root.

## Configuration (Required)

The following environment variables are required. Missing values raise a
`ValueError` at startup:
These env vars are required to run CASTS with real embedding + LLM services
(typically used by the Python harness). Missing values raise a `ValueError`
when those services are instantiated:

- `EMBEDDING_ENDPOINT`
- `EMBEDDING_APIKEY`
Expand All @@ -45,12 +72,16 @@ automatic fallbacks for missing credentials.

## Real Data Loading

The default loader reads CSV files from `data/real_graph_data` (or
`real_graph_data`) and builds a directed graph. You can override this behavior
by providing a custom loader:
The default harness loader reads CSV files from:

- `harness/data/real_graph_data` (repo default), or
- `real_graph_data` (alternate path), or
- a configured `GraphGeneratorConfig.real_data_dir`

You can also override loading by providing a custom loader:

```python
from data.graph_generator import GraphGeneratorConfig, GraphGenerator
from harness.data.graph_generator import GraphGeneratorConfig, GraphGenerator

def my_loader(config: GraphGeneratorConfig):
# return nodes, edges
Expand All @@ -65,26 +96,89 @@ graph = GraphGenerator(config=config)
`InMemoryGraphSchema` caches type-level labels. If you mutate nodes or edges
after creation, call `mark_dirty()` or `rebuild()` before querying schema data.

## Local Dev (Python 3.11 + uv)

In this repo, each Python plugin keeps its own virtual environment at `.venv/`
(gitignored).

One-time venv creation:

```bash
cd geaflow-ai/plugins/casts
[ -d .venv ] || python3.11 -m venv .venv
```

Sync dependencies:

```bash
cd geaflow-ai/plugins/casts
uv sync --extra dev
```

Notes:

- You don't need to `source .venv/bin/activate` for normal workflows.
- `uv sync` installs into the project env (`.venv/`)
- `uv run ...` executes inside that env
- If you *do* activate a venv for interactive work, `uv sync --active` forces syncing
into the active environment.

## Running a Simulation

From the plugins directory (parent of this module):
From the CASTS plugin directory:

```bash
cd geaflow-ai/plugins/casts
uv sync --extra harness
uv run python -m harness.simulation.runner
```

## Running the Service (FastAPI)

Start the CASTS decision service (used by GeaFlow Java integration):

```bash
cd geaflow-ai/plugins/casts
uv sync --extra dev
uv run uvicorn api.app:app --host 127.0.0.1 --port 5001
```

Java side configuration (defaults shown):

- `GEAFLOW_AI_CASTS_URL=http://localhost:5001`
- `GEAFLOW_AI_CASTS_TOKEN=` (optional bearer token)

One-click smoke test (starts/stops the server as needed):

```bash
cd /Users/kuda/code/geaflow/geaflow-ai/plugins
uv sync
python -m simulation.runner
cd geaflow-ai/plugins/casts
./scripts/smoke.sh
```

## Tests

Run tests locally:

```bash
uv sync
pytest
cd geaflow-ai/plugins/casts
uv sync --extra dev
uv run pytest -q
```

## Lint & Type Check

Run lint (ruff) and type checks (mypy):

```bash
cd geaflow-ai/plugins/casts
uv sync --extra dev
uv run ruff format --check .
uv run ruff check .
uv run mypy -p api -p core -p services -p harness
```

There is no GitHub Actions workflow for this module by default.
CI: `.github/workflows/ci-py311.yml` runs the CASTS + LightMem Python tests on
Python 3.11 via `uv`.

## Documentation

Expand Down
18 changes: 18 additions & 0 deletions geaflow-ai/plugins/casts/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

"""CASTS HTTP API (FastAPI)."""
Loading