From afeec9963c06a0d2c37197875427b740c029619b Mon Sep 17 00:00:00 2001 From: DevStrikerTech Date: Wed, 8 Apr 2026 19:39:11 +0100 Subject: [PATCH 1/2] docs: clarify chore branches and version-only bump naming Made-with: Cursor --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 161eaee..bee0328 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,9 +14,12 @@ Thanks for contributing to Oris. | `prod` | Production-ready code | | `dev` | Integration branch; all feature PRs land here | | `feat/*` | Feature branches only (from `dev`) | +| `chore/*` | Tooling, release housekeeping, **metadata-only** changes (from `dev`) | Use `fix/` for bugfix branches when clearer than `feat/`. +For **version-only** bumps (e.g. `pyproject.toml` `version = …`), use a **`chore/`** branch such as `chore/bump-0.7.1`—not `feat/release-*`, which reads like product feature work. PR titles should state that the change is **metadata-only** and that **tag + PyPI** are separate steps after `dev` → `prod`. + ## Project board Use the **[Oris GitHub Project](https://github.com/users/DevStrikerTech/projects/5)** (linked to this repository). **Status** values include **Tinkering** (draft or exploratory work) and **Reviews** (ready for review). `.github/workflows/pr-automation.yml` labels PRs into `dev` and updates the project board when the Actions token (or optional repo secret `ORIS_PROJECTS_WRITE_TOKEN`) can access the Project API. Link issues or project items in the PR template when it helps tracking. From 2b7a423ba3bf33229dbb2ccf8b8ad4b0b0f7d9b3 Mon Sep 17 00:00:00 2001 From: DevStrikerTech Date: Wed, 8 Apr 2026 20:54:48 +0100 Subject: [PATCH 2/2] =?UTF-8?q?chore:=20release=200.7.2=20=E2=80=94=20MkDo?= =?UTF-8?q?cs=20site,=20notebooks,=20README=20polish?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Version 0.7.2 (pyproject, mkdocs extra.version) - Professional docs (Material): intro, get-started, concepts, guides, community - GitHub Pages workflow (docs.yml); CI MkDocs strict job - Example notebooks; consolidated docs logo under docs/images - README Haystack-style layout; removed duplicate root markdown in favor of docs - CONTRIBUTING/SECURITY updates Made-with: Cursor --- .github/SETUP_PRIVATE_REPO.md | 59 ---- .github/workflows/ci.yml | 13 + .github/workflows/{oris-docs.yml => docs.yml} | 23 +- .gitignore | 12 +- ARCHITECTURE.md | 64 ----- CONTRIBUTING.md | 128 ++++----- ENGINEERING_STANDARDS.md | 64 ----- PROVIDER_DESIGN.md | 249 ----------------- PUBLIC_API.md | 78 ------ README.md | 242 +++++++++++------ RELEASE.md | 43 --- SECURITY.md | 59 ++-- docs/concepts/components.md | 40 +++ docs/concepts/overview.md | 29 ++ docs/concepts/pipelines.md | 46 ++++ docs/concepts/rai-tracing.md | 29 ++ docs/development/code-of-conduct.md | 7 + docs/development/contributing.md | 32 +++ docs/development/security.md | 20 ++ docs/examples/index.md | 35 +++ docs/get-started/installation.md | 47 ++++ docs/get-started/quickstart.md | 66 +++++ docs/guides/cli.md | 50 ++++ docs/guides/output.md | 33 +++ docs/guides/safe-runner.md | 54 ++++ docs/{ => images}/oris_logo.png | Bin docs/index.md | 86 ++---- examples/basic_pipeline.ipynb | 193 +++++++++++++ examples/llm_integration.ipynb | 257 ++++++++++++++++++ examples/safe_runner.ipynb | 138 ++++++++++ mkdocs.yml | 91 +++++-- pyproject.toml | 8 +- scripts/sync_doc_sources.sh | 9 - 33 files changed, 1445 insertions(+), 859 deletions(-) delete mode 100644 .github/SETUP_PRIVATE_REPO.md rename .github/workflows/{oris-docs.yml => docs.yml} (70%) delete mode 100644 ARCHITECTURE.md delete mode 100644 ENGINEERING_STANDARDS.md delete mode 100644 PROVIDER_DESIGN.md delete mode 100644 PUBLIC_API.md delete mode 100644 RELEASE.md create mode 100644 docs/concepts/components.md create mode 100644 docs/concepts/overview.md create mode 100644 docs/concepts/pipelines.md create mode 100644 docs/concepts/rai-tracing.md create mode 100644 docs/development/code-of-conduct.md create mode 100644 docs/development/contributing.md create mode 100644 docs/development/security.md create mode 100644 docs/examples/index.md create mode 100644 docs/get-started/installation.md create mode 100644 docs/get-started/quickstart.md create mode 100644 docs/guides/cli.md create mode 100644 docs/guides/output.md create mode 100644 docs/guides/safe-runner.md rename docs/{ => images}/oris_logo.png (100%) create mode 100644 examples/basic_pipeline.ipynb create mode 100644 examples/llm_integration.ipynb create mode 100644 examples/safe_runner.ipynb delete mode 100755 scripts/sync_doc_sources.sh diff --git a/.github/SETUP_PRIVATE_REPO.md b/.github/SETUP_PRIVATE_REPO.md deleted file mode 100644 index a99e2e5..0000000 --- a/.github/SETUP_PRIVATE_REPO.md +++ /dev/null @@ -1,59 +0,0 @@ -# Private GitHub repository: `oris` - -The project is **not** public at this stage. Create the remote as **private**. - -## Create the repository - -1. GitHub → **New repository** -2. **Repository name:** `oris` -3. Visibility: **Private** -4. Do **not** add README, `.gitignore`, or license (this repo already has them) - -## Add remote and push - -Replace `YOUR_ORG` with your user or organization: - -```bash -git remote add origin git@github.com:YOUR_ORG/oris.git -git push -u origin prod -git push -u origin dev -``` - -Set the **default branch** on GitHub to **`dev`** (Settings → General → Default branch) so new PRs and clones align with the integration workflow. - -## Branch protection (required) - -Configure in **Settings → Branches → Add rule**. - -### `prod` - -- Require a pull request before merging -- Require status checks to pass (select the **CI** workflow / `quality` job) -- Require branches to be up to date before merging -- **Do not** allow force pushes -- Restrict who can push (optional: admins only) - -### `dev` - -- Require a pull request before merging -- Require status checks to pass (CI) -- **Do not** allow direct pushes from contributors (use rulesets or team settings as appropriate) - -### Enforcing `feat/*` only for PRs - -Use **Rulesets** (or branch name patterns) so contributors cannot push to `dev`/`prod` without a PR. Feature work must use `feat/` (or `fix/`) branched from `dev`. - -## GitHub Pages (documentation site) - -The **Oris docs** workflow (`.github/workflows/oris-docs.yml`) publishes [MkDocs](https://www.mkdocs.org/) output when **`prod`** is updated. Deployments use the **`oris-docs`** environment (not the generic `github-pages` name). - -1. **Settings → Pages** -2. **Build and deployment → Source:** **GitHub Actions** -3. **Settings → Environments → `oris-docs`:** leave **no required reviewers** unless you want manual approval before each docs deploy. -4. After the first successful run, the site is at **`https://.github.io/oris/`** (e.g. `devstrikertech.github.io/oris`). - -**Visibility:** On **GitHub Free**, Pages for a **private** repository may be unavailable or restricted; use a **public** repo or a **Pro/Team/Enterprise** plan for private Pages. See [GitHub Pages documentation](https://docs.github.com/en/pages/getting-started-with-github-pages/github-pages-limits). - -## Secrets - -Do not store API keys in the repository. Use GitHub **Secrets** only when CI/CD needs authenticated steps later. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7810ad8..5b52661 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,19 @@ concurrency: cancel-in-progress: true jobs: + docs: + name: MkDocs (strict) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install MkDocs Material + run: pip install "mkdocs-material>=9.5.0" + - name: Build documentation + run: mkdocs build --strict + quality: runs-on: ubuntu-latest strategy: diff --git a/.github/workflows/oris-docs.yml b/.github/workflows/docs.yml similarity index 70% rename from .github/workflows/oris-docs.yml rename to .github/workflows/docs.yml index 9eb0bc8..78503ff 100644 --- a/.github/workflows/oris-docs.yml +++ b/.github/workflows/docs.yml @@ -1,7 +1,7 @@ -# Publishes https://devstrikertech.github.io/oris/ via GitHub Pages (build_type: workflow). -# Repo Settings → Pages → Source: GitHub Actions +# Publishes https://devstrikertech.github.io/oris/ via GitHub Pages (workflow source). +# Repository Settings → Pages → Build and deployment: GitHub Actions -name: Oris docs +name: Docs on: push: @@ -14,12 +14,12 @@ permissions: id-token: write concurrency: - group: oris-docs + group: pages-docs cancel-in-progress: false jobs: - build-oris-docs: - name: Build documentation + build: + name: Build MkDocs site runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -28,12 +28,9 @@ jobs: with: python-version: "3.11" - - name: Install MkDocs + - name: Install MkDocs Material run: pip install "mkdocs-material>=9.5.0" - - name: Sync root markdown into docs/ - run: bash scripts/sync_doc_sources.sh - - name: Build site run: mkdocs build --strict @@ -41,12 +38,12 @@ jobs: with: path: site - deploy-oris-docs: + deploy: name: Deploy to GitHub Pages - needs: build-oris-docs + needs: build runs-on: ubuntu-latest environment: - name: oris-docs + name: github-pages url: ${{ steps.deployment.outputs.page_url }} permissions: pages: write diff --git a/.gitignore b/.gitignore index b2cbd61..ad54633 100644 --- a/.gitignore +++ b/.gitignore @@ -17,12 +17,6 @@ build/ *.egg-info/ site/ -# MkDocs: copies of root *.md produced by scripts/sync_doc_sources.sh (CI runs this; do not commit) -docs/ARCHITECTURE.md -docs/ENGINEERING_STANDARDS.md -docs/SECURITY.md -docs/PUBLIC_API.md -docs/RELEASE.md -docs/CONTRIBUTING.md -docs/CODE_OF_CONDUCT.md -docs/PROVIDER_DESIGN.md +# Jupyter +.ipynb_checkpoints/ +.jupyter/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 1cccf30..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,64 +0,0 @@ -# Oris Architecture - -## Goals - -Oris is built as a modular Responsible AI runtime for production deployments: - -- enforce safety controls by default -- remain framework-agnostic -- provide deterministic and testable execution -- support auditability and extensibility - -## High-Level Modules - -- `core/`: shared enums, exceptions, and domain primitives. -- `components/`: abstract component contract and registry for pipeline steps. -- `runtime/`: executor, orchestrator, execution context, hook ABCs, `StepRunner`, `TraceManager`, and result model. -- `rai/`: policy enforcement, input guard, output guard. -- `providers/`: LLM provider abstraction and backend stubs. -- `integrations/`: wrappers for external runtimes (`SafeRunner`). -- `tracing/`: run and step traces, audit logger. -- `cli/`: command-line operations (`run`, `validate`). -- `api/`: reserved for API adapters. - -## Execution Flow - -1. Load pipeline config from YAML or in-memory mapping. -2. Validate schema and component declarations. -3. Build components from registry; each component’s `validate_config(config)` runs at construction (via `__post_init__`). -4. `RuntimeExecutor` calls `TraceManager.begin_run()` for the only `RunTrace` construction path used by the executor; it builds `ExecutionContext` (run id, plan metadata, shared trace, injected `PolicyEnforcer`, step pointers). -5. Run **pipeline pre-hooks** (default: `InputPolicyHook` as `PipelinePreHook`, then optional RAI pre-hooks). Each hook implements `Hook.invoke(data, context)`; plain callables are wrapped at executor construction. -6. For each plan step: **pre-step hooks** (`PreStepHook`) → component (via `PipelineOrchestrator` and `Component.run`) → **post-step hooks** (`PostStepHook`). `TraceManager.traced_hook` records hook steps; `TraceManager.traced_component` records the component body (success or failure) so step traces are not assembled outside `TraceManager`. -7. Run **pipeline post-hooks** (optional RAI post-hooks, then default `OutputPolicyHook` as `PipelinePostHook`). -8. `TraceManager.finalize_success` / `finalize_failure` close the run; run metadata attachment for summaries is applied inside `finalize_success` when provided. - -## Design Decisions - -- **Safety-first runtime**: default pipeline hooks enforce input/output policy; overrides are explicit via `pipeline_pre_hooks` / `pipeline_post_hooks`. -- **Framework independence**: `SafeRunner` uses a protocol, not framework imports; policy is injected and uses the same `PolicyEnforcer` entry points as default pipeline hooks. -- **Strict typing**: complete type hints and strict static analysis (`mypy --strict`). -- **Explicit failure states**: domain-specific exception hierarchy; hook kind mismatches raise `TypeError` at executor construction when a wrong `Hook` subclass is used. -- **No global mutable state**: dependency injection through constructors. -- **ExecutionContext** is a small, documented surface (`run_id`, `metadata`, `trace`, `policy`, `current_step_id`, `step_index`); extend runs via `metadata` keys rather than ad-hoc bags. - -## Extensibility Model - -- Register custom components via `ComponentRegistry`; override `validate_config(self, config)` with explicit dict validation. -- Add new providers by implementing `LLMProvider`. -- Replace `PipelineOrchestrator` with custom execution strategies. -- Extend policy checks through `PolicyEnforcer` or register hooks: subclass `PreStepHook`, `PostStepHook`, `PipelinePreHook`, or `PipelinePostHook`, or pass callables (wrapped to `Callable*Hook`). -- Use `InputGuard` / `OutputGuard` as components when not using the default hook chain. - -## Security Boundaries - -- YAML parsing is performed using `yaml.safe_load`. -- config is validated before execution. -- audit logs redact known sensitive keys. -- credentials are expected from environment variables or secret stores. - -## Future Evolution (Non-Breaking) - -- stronger schema validation for pipeline documents -- richer policy engines (tenant- and domain-specific) -- trace export adapters (OpenTelemetry, SIEM pipelines) -- signed policy bundles and provenance verification diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bee0328..5c4ca65 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,68 +1,54 @@ # Contributing to Oris -Thanks for contributing to Oris. +Thanks for helping improve Oris. -## Repository policy +## Setup -- The **GitHub repository must remain private** until explicitly opened. Do not publish the package or code publicly without review. -- Repository name on GitHub: **`oris`**. - -## Branching model - -| Branch | Purpose | -| -------- | -------------------------------------------- | -| `prod` | Production-ready code | -| `dev` | Integration branch; all feature PRs land here | -| `feat/*` | Feature branches only (from `dev`) | -| `chore/*` | Tooling, release housekeeping, **metadata-only** changes (from `dev`) | - -Use `fix/` for bugfix branches when clearer than `feat/`. - -For **version-only** bumps (e.g. `pyproject.toml` `version = …`), use a **`chore/`** branch such as `chore/bump-0.7.1`—not `feat/release-*`, which reads like product feature work. PR titles should state that the change is **metadata-only** and that **tag + PyPI** are separate steps after `dev` → `prod`. - -## Project board - -Use the **[Oris GitHub Project](https://github.com/users/DevStrikerTech/projects/5)** (linked to this repository). **Status** values include **Tinkering** (draft or exploratory work) and **Reviews** (ready for review). `.github/workflows/pr-automation.yml` labels PRs into `dev` and updates the project board when the Actions token (or optional repo secret `ORIS_PROJECTS_WRITE_TOKEN`) can access the Project API. Link issues or project items in the PR template when it helps tracking. - -## Development workflow +```bash +git clone https://github.com/DevStrikerTech/oris.git +cd oris +python -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e ".[dev]" +``` -For **any** new work: +Optional: install pre-commit so local commits match CI. -1. Branch from **`dev`**: `git checkout dev && git pull && git checkout -b feat/` -2. Implement and commit **only** on that feature branch -3. Open a **pull request into `dev`** -4. Merge to **`dev`** only after CI passes and review -5. **Daily (or on cadence):** merge **`dev` → `prod`** only when CI is green and validation is complete +```bash +pre-commit install +``` -## Strict rules +Pre-commit runs Ruff, Ruff format, Mypy, pytest (with the repo coverage gate via `scripts/precommit-pytest.sh`), and detect-secrets (baseline: `.secrets.baseline`). -- **No direct commits** to `dev` or `prod` -- **No merge** without passing CI -- **No merge** without tests appropriate to the change -- **No force push** to `prod` +## Branch workflow -GitHub branch protection and rulesets should enforce the above; see [`.github/SETUP_PRIVATE_REPO.md`](https://github.com/DevStrikerTech/oris/blob/dev/.github/SETUP_PRIVATE_REPO.md). +| Branch | Purpose | +| :--- | :--- | +| `prod` | Release-ready history | +| `dev` | Integration branch; feature PRs target this | +| `feat/*` | Feature branches (from `dev`) | +| `fix/*` | Bugfix branches when clearer than `feat/*` | +| `chore/*` | Tooling and metadata-only changes (from `dev`) | -## Quality gates (every commit) +Workflow: -All of the following must pass before a commit is acceptable: +1. Branch from `dev`: `git checkout dev && git pull && git checkout -b feat/your-feature` +2. Implement with focused commits +3. Open a PR into `dev` +4. Merge after CI passes and review -- Tests cover behavior changes; **coverage stays ≥ 84%** (`pytest` / `pyproject.toml`) -- **Ruff** lint and format (`ruff check src tests`, `ruff format --check src tests`) -- **Mypy** (`mypy src/oris tests`) +Do not commit secrets or credentials. Prefer environment variables or your platform’s secret store for anything sensitive. -Install **pre-commit** inside your project virtualenv so hooks use the same Python and dependencies: +## Coding standards -```bash -python -m venv .venv -source .venv/bin/activate # Windows: .venv\Scripts\activate -pip install -e ".[dev]" -pre-commit install -``` +- **Typing:** Full type hints; the repo uses **mypy strict** (`mypy src/oris tests`). +- **Style:** **Ruff** lint + format (`ruff check src tests`, `ruff format --check src tests`). +- **Boundaries:** Keep changes inside the relevant package area (`pipeline`, `runtime`, `rai`, etc.); avoid leaking internals through new public APIs without discussion. +- **Commits:** Small and scoped; use a type prefix, e.g. `feat:`, `fix:`, `test:`, `docs:`, `chore:`. -Pre-commit runs **ruff**, **ruff-format**, **mypy**, **pytest** (with the repo coverage gate via `scripts/precommit-pytest.sh`, preferring `.venv`), and **detect-secrets** (baseline: `.secrets.baseline`). If any hook fails, fix the issue before committing. +## Quality gates -## Local quick check +These must pass locally (and in CI) before a change is ready: ```bash ruff check src tests @@ -71,33 +57,35 @@ mypy src/oris tests pytest ``` -## Coding rules +Coverage is enforced with **pytest-cov**; the minimum line coverage is **84%** (see `pyproject.toml`). -- Maintain strict typing. -- Follow module boundaries described in `ARCHITECTURE.md`. -- Keep commits **small and focused**; avoid unrelated changes. -- Add or update tests for all behavior changes. -- Update public API docs when changing exposed interfaces. +## Testing requirements -## Commit message format +- Add or update **tests** for any behavior change (`tests/` mirrors `src/oris` where possible). +- Run the full suite with `pytest`; do not lower the coverage gate without maintainer agreement. +- For CLI or tracing changes, consider `tests/test_cli*.py` and `tests/test_tracing.py`. -Use a **type prefix** and a short, imperative description: +## Pull requests -- `feat: add pipeline validation for missing component type` -- `fix: correct step trace latency on guard failure` -- `refactor: split pipeline loader from validation` -- `test: cover provider credential env resolution` -- `chore: update CI Python matrix` +Use the [PR template](.github/PULL_REQUEST_TEMPLATE.md). Typical checklist: -## Pull request checklist - -- [ ] Target branch is **`dev`** -- [ ] Source branch is **`feat/*`** or **`fix/*`** -- [ ] Tests added/updated -- [ ] Docs updated where behavior is user-visible -- [ ] `ruff`, `mypy`, `pytest` pass locally +- [ ] Target branch is `dev` +- [ ] Tests added or updated for behavior changes +- [ ] Docs or examples updated if users would notice the change +- [ ] Ruff, Mypy, and pytest pass - [ ] No secrets in the diff +## Documentation site + +User-facing docs live under **`docs/`** and are built with **MkDocs Material** (`mkdocs.yml`). Preview locally: + +```bash +pip install -e ".[docs]" +mkdocs serve +``` + +The **Docs** workflow (`.github/workflows/docs.yml`) publishes to GitHub Pages on pushes to **`prod`**. + ## Code of Conduct -By participating, you agree to follow `CODE_OF_CONDUCT.md`. +Participation is governed by [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). diff --git a/ENGINEERING_STANDARDS.md b/ENGINEERING_STANDARDS.md deleted file mode 100644 index 8b58a95..0000000 --- a/ENGINEERING_STANDARDS.md +++ /dev/null @@ -1,64 +0,0 @@ -# Engineering Standards - -## Purpose - -This document defines non-negotiable engineering rules for Oris contributors. - -## Code Quality Rules - -- Python code must include complete type hints. -- `mypy --strict` must pass before merge. -- `ruff` linting must pass with no errors. -- New features require tests and documentation updates. -- Cyclomatic complexity should remain low; prefer decomposition into small units. - -## Design Principles - -- Follow SOLID principles. -- Favor interfaces and abstract base classes at module boundaries (e.g. `Component`, runtime `Hook` hierarchy, `ExternalRunnable` for integrations). -- Avoid hidden side effects and global mutable state. -- Keep modules decoupled and dependency direction inward toward `core`. -- Prefer composition over inheritance unless hierarchy is required. -- **Components**: implement `validate_config(self, config: dict[str, Any])` with explicit dict validation; the base class invokes it with `self.config` at construction. -- **Hooks**: implement `invoke(self, data, context)` on the appropriate hook ABC (`PreStepHook`, `PostStepHook`, `PipelinePreHook`, `PipelinePostHook`); runtime hooks normalize plain callables via `as_*_hook` helpers. -- **Tracing**: create and mutate run/step traces only through `TraceManager` in the execution path; do not build `StepTrace` records ad hoc outside it. -- **ExecutionContext**: keep fields limited to run identity, plan metadata, trace, policy, and current step pointers; use `metadata` for documented cross-cutting keys. - -## Testing Requirements - -- Use `pytest` for all tests. -- Add both unit and integration tests for runtime behavior. -- Keep minimum total coverage at **84%**. -- Test security-relevant behavior (guards, validation, redaction) explicitly. - -## Pull Request Expectations - -- One logical change per PR. -- PR must include: - - clear problem statement - - design rationale - - test evidence - - backward compatibility impact -- CI must pass before review and merge. - -## Branch Workflow Policy - -- `prod`: production branch. -- `dev`: integration branch. -- `feat/*`: feature branches. - -Hard rules: - -- no direct commits to `dev` or `prod` -- PRs required for all merges -- status checks required before merge -- daily merge from `dev` to `prod` after verification - -## Definition of Done - -A change is done when: - -1. code, tests, docs, and release notes are updated -2. quality checks and security scans pass -3. API compatibility impact is documented -4. reviewer approval is recorded diff --git a/PROVIDER_DESIGN.md b/PROVIDER_DESIGN.md deleted file mode 100644 index 1be32ee..0000000 --- a/PROVIDER_DESIGN.md +++ /dev/null @@ -1,249 +0,0 @@ -# Provider and Configuration System — Design - -This document specifies the architecture for Oris’s **provider abstraction**, **declarative configuration**, **environment resolution**, **registry**, and **runtime integration**. It is intended for review **before** implementation work begins. - -**Scope:** architecture and contracts only. No full implementation, runtime changes, or concrete provider SDK logic are specified here. - ---- - -## V1 Decisions - -The following choices are **fixed for V1** so implementation and reviews stay aligned: - -- **Eager validation and construction:** During **pipeline build**, every declared entry under `providers` is **fully validated** (structure, registry `type`, type-specific rules, unknown keys—see §2) and each **`LLMProvider` instance is constructed** immediately. There is **no lazy** “construct on first use” in V1. The same rules apply on `oris validate` and `oris run` so failures surface before execution. - -- **Preferred secret pattern:** Use **environment indirection** via `api_key_env: OPENAI_API_KEY` (or another variable name), not inline secrets in YAML. This is the **default guidance** in examples and docs. - -- **`${ENV_VAR}` expansion:** Supported for **non-secret** configuration where templating helps (e.g. URLs, hostnames). **Examples and guidance should still prefer `api_key_env` for API keys and similar secrets** so secrets rarely pass through expanded-string paths. When `${VAR}` is used and expansion applies, missing variables behave as in §3. - -- **`ExecutionContext`:** Must **not** hold a provider map, **not** act as a provider resolver, and **not** gain provider lookup APIs in V1. Run context remains run metadata, policy, trace, and step pointers only. - -- **Component injection:** Components that need model access receive a **concrete `LLMProvider` instance** (for the resolved logical id) **at construction time** in V1—not a resolver, not a callback, not lazy lookup by id during `run`. - -- **Unknown provider keys:** Under each `providers.` declaration, **any key not explicitly allowed for that provider `type` is a validation error** in V1. Relaxation (warn-only or passthrough) is a **post-V1** discussion unless a given `type` documents optional extra keys. - ---- - -## 1. Provider Abstraction Design - -### Base interface: `LLMProvider` - -The framework exposes an abstract **LLM provider** contract that represents “something that can produce model output from a prompt (and optional generation parameters).” Conceptually it is a **thin adapter** between Oris execution and a backend (hosted API, local inference server, mock, etc.). - -**Responsibilities** - -- Accept a **textual prompt** (and optionally structured kwargs such as temperature, max tokens, stop sequences) and return **generated text** (or a small, documented result type if we later standardize beyond raw strings). -- Hold **non-secret configuration** needed to address the backend (e.g. model id, base URL for OpenAI-compatible endpoints). -- Expose a **stable, minimal surface** so components and tests can depend on behavior, not on vendor SDKs. - -**What the provider should NOT handle** - -- **Pipeline orchestration**, step ordering, or data flow between components. -- **RAI policy** (input/output guards, policy enforcement hooks). Those remain in `rai/` and hooks; the provider only performs the model call it is asked to perform. -- **Tracing semantics**: the provider does not own run or step traces. The runtime may wrap calls to record latency or errors, but the provider stays unaware of `TraceManager` details. -- **Secrets as persistent state in config blobs**: API keys and tokens must not be required to live inside validated YAML as plain text; V1 prefers **`api_key_env`** indirection (see **V1 Decisions** and §7). `${VAR}` expansion exists for other fields (§3). -- **YAML parsing or schema validation** for the whole pipeline document. -- **Retry/backoff policy** as a hard requirement inside the base class (orchestration-level or a dedicated policy layer may decide this later; the base contract should not force one strategy). - ---- - -## 2. Provider Configuration Model - -### YAML structure - -Top-level pipeline documents gain an optional **`providers`** mapping: each **key** is a **logical provider id** (how components refer to it). Each **value** is a **provider declaration**: at minimum a `type` field that selects the implementation, plus type-specific fields. - -Example (illustrative): - -```yaml -name: my_pipeline - -providers: - openai_default: - type: openai - model: gpt-4 - api_key_env: OPENAI_API_KEY - - cheap_local: - type: openai_compatible - base_url: http://127.0.0.1:8080/v1 - model: llama-3 - api_key_env: LOCAL_LLM_API_KEY - -components: - - type: some_llm_step - name: answer - config: - provider: openai_default -``` - -**Structure (normative intent)** - -- `providers`: optional `dict[str, dict]`. -- Each entry: `type: str` (registry key for the factory), plus **only** scalar/mapping fields on that type’s **documented allowlist** (V1: unknown keys are errors—see **V1 Decisions**). -- Components reference providers by **logical id** (e.g. `provider: openai_default`), not by Python import paths, keeping YAML portable and registry-driven. - -**Validation** - -- **Structural:** `providers` values must be mappings; `type` must be present and non-empty. -- **Registry:** `type` must resolve to a registered provider factory at load/build time (clear error if unknown). -- **Type-specific / allowlist:** each registered provider declares permitted keys and validates required fields (`model`, URLs, numeric ranges, etc.). **Unknown keys are rejected in V1.** Fail fast at pipeline build / `validate` CLI, not at first `run`. -- **References:** any `provider: ` in LLM component config must refer to an id defined under `providers` (**V1:** no implicit or pipeline-level default id). - -**Extensibility** - -- New backends add a new `type` string and factory without changing the global YAML shape. -- Provider-specific keys live **namespaced** under that declaration; the core schema only requires `type` and stable conventions (e.g. optional `api_key_env`). -- **V1:** Each provider `type` documents an **allowlist** of permitted keys; **unknown keys are rejected** (see **V1 Decisions**). Post-V1, optional policies (warn-only, passthrough) could be reconsidered without changing the YAML grammar. - ---- - -## 3. Environment Variable Handling - -### Two mechanisms (V1) - -1. **`api_key_env` (and similar documented fields):** The value is the **name** of an environment variable; the provider **factory reads `os.environ` at construction time** (during eager pipeline build). **This is the preferred pattern for secrets**; examples should use it (see **V1 Decisions**). - -2. **`${ENV_VAR}` expansion:** Selected string values in the provider declaration may use placeholders `${VAR_NAME}` for **non-secret** configuration (e.g. `base_url: http://${LLM_HOST}:8080/v1`). Expansion runs in the configuration layer; do **not** rely on `${...}` for API keys in documentation—use `api_key_env` instead. - -### `${ENV_VAR}` resolution rules - -- Run **after** `yaml.safe_load`, **before** provider factory runs, as part of the same **eager build** path as validation (§5). -- **V1 semantics:** support **whole-string** substitution (the entire YAML scalar equals one `${VAR_NAME}` token). **Substring / multi-placeholder templates** are out of scope for V1 unless explicitly added later with a spec. -- **Default interpolation** (e.g. `${VAR:-default}`) is **not** in V1. - -**Where expansion runs** - -- A dedicated **env expander** in the **configuration / pipeline build** layer walks **allowlisted** keys under each `providers.` entry (exact key list per `type` is part of each provider’s documented schema). Only declared expandible fields are transformed; others are left as literals for the factory. -- After expansion, factories still receive **`api_key_env` as a variable name string** where that key is used; they load the secret from the environment when building the instance—expanded strings and `api_key_env` are not interchangeable. - -**Missing variables** - -- For **`${VAR}`** on an expandible field: if the variable is **unset or empty**, raise **`ConfigurationError`** (or subclass) with **variable name** and **config path** (e.g. `providers.cheap_local.base_url`); **never** echo a resolved secret. -- For **`api_key_env`**: if the named variable is **missing or empty** when the factory requires a credential, fail at **the same eager build** step with a clear error (path + env name, no secret value). -- **`oris validate`** and **`oris run`** use the **identical** build path so these failures occur before any step executes. - ---- - -## 4. Provider Registry Design - -### Registration - -- A **central registry** maps string `type` → **factory** (callable or small class) that builds an `LLMProvider` instance from: - - the **merged** declaration dict for that provider id (after `${VAR}` expansion on allowlisted fields only; `api_key_env` left as a name for the factory to resolve), and - - optional **framework defaults** (e.g. timeout defaults) injected at build time, not from YAML. -- **Built-in** types (`openai`, `openai_compatible`, `huggingface`, etc.) register at package import or via an explicit `register_builtin_providers()` to keep discovery predictable. -- **User code** registers custom types before `Pipeline.from_yaml` / `from_config` (or via a documented entry-point hook if added later). - -### Resolution - -- Load YAML → validate top-level structure → expand `${VAR}` on allowlisted fields → **reject unknown keys** per `type` → for each `providers.`, read `type` → look up factory → run type-specific validation → **construct `LLMProvider` immediately** (V1: eager only; see **V1 Decisions** and §5). -- Unknown `type`: fail with message listing **similar** names or **available** types (developer experience). - -### Adding new providers - -1. Implement `LLMProvider` (or the finalized base contract). -2. Implement a factory that accepts the declaration dict and returns an instance. -3. Register `type: "my_vendor"` with the registry (one line in app startup or in an integrations module). -4. Document supported YAML keys and env vars for that type. - -No changes to core YAML grammar are required for most new backends. - ---- - -## 5. Runtime Integration - -### How the runtime obtains provider config - -- **Pipeline build** (shared by `Pipeline.from_yaml` / `from_config`, `oris validate`, and `oris run`) parses the document, validates `providers`, applies `${VAR}` expansion per §3, runs **allowlisted-key** and **type-specific** validation, then **constructs every declared provider**. -- The built pipeline holds a **logical id → `LLMProvider` instance** map (internal detail; naming TBD at implementation). The **executor / orchestrator** does **not** re-parse YAML or re-resolve env for providers; it uses the already-built pipeline graph. - -### How provider instances are created (V1) - -- **Eager only:** each `providers.` entry is validated and instantiated **during pipeline build**. Unused providers are still constructed in V1 (predictable failures, simpler mental model). -- **No lazy construction** in V1. - -Instances must be **immutable enough** for concurrent read use across steps (no per-run mutable global state on the provider object; per-call kwargs hold call-specific options). - -### Injection into components (V1) - -- The **pipeline builder** resolves each component’s `provider: ` to the **already-built `LLMProvider`** and passes that **concrete instance** into the component constructor (or equivalent factory kwargs). -- **`ExecutionContext` must not** expose provider lookup, hold provider maps, or act as a resolver (**V1 Decisions**). Executor code passes `ExecutionContext` into `Component.run` unchanged; provider references exist only on the component object. - ---- - -## 6. Component Interaction - -### Referencing providers - -- Component YAML uses a **stable key** (e.g. `provider: `) under `config`. -- **V1:** every LLM-using component **must** declare `provider` explicitly. **No** pipeline-level default provider id in V1 (avoids implicit wiring). - -### Passing providers to components - -- During pipeline build, the builder maintains **logical id → `LLMProvider`** for declared providers. When constructing a component that references `provider: openai_default`, the builder passes the **same instance** wired for that id. -- LLM-using components **fail validation at build time** if `provider` is missing, unknown, or not declared under `providers`. -- Non-LLM components are constructed **without** provider arguments. - -### Separation of concerns - -| Layer | Responsibility | -|--------|----------------| -| YAML + loader | Syntax, safe parse, file existence | -| Env expander | `${VAR}` on allowlisted provider fields; missing-var errors | -| Provider registry | `type` → factory; allowlisted keys only; construct instances | -| Pipeline builder | Resolve `provider` id → instance; **inject instance** into components | -| Component | Hold `LLMProvider` reference; use provider API in `run`; map `data` ↔ prompt | -| `ExecutionContext` | Run metadata, policy, trace, step pointers—**no providers** (V1) | -| Runtime executor | Steps, hooks, tracing, policy—not vendor APIs | -| Provider | Backend call only | - ---- - -## 7. Security Considerations - -### Secrets handling - -- **V1 standard:** `api_key_env: OPENAI_API_KEY` (or per-backend equivalent documented for the `type`). **Do not** document API-key-via-`${VAR}` as the happy path; reserve `${VAR}` for non-secret scalars (§3). -- If a future field legitimately expands to sensitive data, treat expanded values like secrets: **never** persist in traces, run summaries, or debug dumps. -- In-memory provider/config graphs must not be serialized by default into `PipelineResult.metadata`. - -### What must never be logged - -- API keys, tokens, OAuth refresh tokens, signing secrets. -- Raw `${...}` resolution output when the value is a known credential field (mask in any diagnostic path). -- Full request/response bodies from vendor APIs if they contain PII; tracing should record **hashes, sizes, or redacted excerpts** per existing audit guidelines. - -### Validation rules - -- **V1:** Prefer **no** raw `api_key` in YAML unless a provider `type` explicitly documents it; even then, steer users to `api_key_env`. Reject empty credentials where a key is required. -- URLs: optional allowlist/denylist hooks for enterprise deployments (future). -- All user-controlled strings passed to HTTP clients undergo normal URL/SSRF policy if the stack adds it (document as integration concern). - ---- - -## 8. Future Extensibility - -### New hosted providers - -- Add `type`, factory, and documentation; keep the `providers:` block and `LLMProvider` method signatures backward compatible. -- Prefer **optional** parameters and **default** behaviors in the base class so old providers keep working. - -### Local models - -- Introduce types such as `llama_cpp`, `ollama`, or `vllm_openai` that implement the same `LLMProvider` interface but point at **localhost** or **Unix sockets**. -- Configuration might include `base_url`, `model`, and resource limits; env expansion applies the same way. - -### Avoiding breaking changes - -- **Additive** YAML keys and optional `LLMProvider` methods (with defaults) or versioned result DTOs. New keys for a `type` require **documentation** on that type’s allowlist so V1 strict validation stays honest. -- **Deprecate** old `type` strings with warnings before removal; maintain registry aliases (`openai` → `openai_chat`) if splitting implementations. -- Public API: new surfaces (registry helpers, builder hooks) should be marked stable only after listing in `PUBLIC_API.md`. **V1** does not require a public `ProviderResolver`; resolution is internal to the builder. - ---- - -## Summary - -Oris should treat **providers** as **small, swappable adapters** declared under `providers`, **validated and constructed eagerly** at pipeline build, and wired by **type-specific factories** behind a **registry**. **Secrets** use **`api_key_env`** by convention; **`${VAR}`** applies only to **allowlisted non-secret** fields under a documented **whole-string** rule. The **pipeline builder** resolves `provider` ids and passes **concrete `LLMProvider` instances** into components; **`ExecutionContext` stays free of provider plumbing**; the **executor** keeps orchestration, policy, and tracing—not vendor SDKs. - -Implementation work following this design should proceed in order: config model + strict per-type allowlists + env expansion → registry → eager construction → builder wiring (instance injection) → component contracts → individual provider backends. diff --git a/PUBLIC_API.md b/PUBLIC_API.md deleted file mode 100644 index c2277b3..0000000 --- a/PUBLIC_API.md +++ /dev/null @@ -1,78 +0,0 @@ -# Public API Contract - -## Stability Scope - -The following interfaces are considered the initial public API for Oris: - -- `oris.Pipeline` -- `oris.runtime.PipelineExecutor` (alias: `RuntimeExecutor`) -- `oris.runtime.PipelineResult` -- `oris.runtime.ExecutionContext` -- `oris.runtime.Hook` and hook ABCs (`PreStepHook`, `PostStepHook`, `PipelinePreHook`, `PipelinePostHook`) plus `Callable*Hook` / `as_*_hook` helpers -- `oris.runtime.TraceManager` -- `oris.integrations.SafeRunner` and `oris.integrations.ExternalRunnable` (typing protocol) -- CLI command `oris` - -Legacy aliases `PreExecutionHook`, `PostExecutionHook`, `ExecutionHook`, and `PipelineHook` remain available for compatibility; prefer the explicit hook names above. - -Anything not listed here is internal and may change between minor versions. - -## Python API - -### `Pipeline` - -- `Pipeline.from_yaml(path: str | Path) -> Pipeline` -- `Pipeline.from_config(config: dict[str, Any]) -> Pipeline` -- `Pipeline.run(input_data: dict[str, object]) -> PipelineResult` - -### `PipelineResult` - -- `output: dict[str, Any]` -- `trace: RunTrace` -- `metadata: dict[str, Any]` - -### `SafeRunner` - -- `SafeRunner(external_pipeline, *, policy: PolicyEnforcer)` -- `ExternalRunnable` (optional typing protocol): objects with `run(input_data: dict[str, Any]) -> Any` -- `run(input_data: dict[str, Any], *, include_trace: bool = False) -> dict[str, Any] | PipelineResult` - - With `include_trace=False` (default): returns `dict[str, Any]`. - - With `include_trace=True`: returns `PipelineResult` with the same fields as `Pipeline.run`, including a minimal run trace (one external step with `latency_ms`, `flags` including `"kind": "external_pipeline"`, and run status). - -`external_pipeline` may be: - -- a callable `def f(data: dict) -> ...`, or -- an object with a callable `run(dict)` method (preferred when both `run` and `__call__` exist). - -`input_data` may be any `collections.abc.Mapping`; it is copied to a plain `dict` before validation. - -Return values are normalized to `dict[str, Any]` when the target returns a `dict`, any `Mapping`, or an object with a duck-typed `model_dump()` that returns a mapping. - -Non-`PipelineExecutionError` exceptions raised inside the external target are surfaced as `PipelineExecutionError("External pipeline execution failed.")` without chaining the original exception as `__cause__`. `GuardViolationError` and other `PipelineExecutionError` subclasses raised by policy or adapters propagate unchanged. - -Validation uses the same `PolicyEnforcer.validate_input` / `validate_output` entry points as the default executor pipeline hooks. - -### Runtime hooks - -- Hooks are objects implementing `invoke(self, data: dict[str, Any], context: ExecutionContext) -> dict[str, Any]` on the appropriate ABC. -- `RuntimeExecutor` accepts hook instances or plain callables (wrapped internally) per parameter: `pre_step_hooks`, `post_step_hooks`, `pipeline_pre_hooks`, `pipeline_post_hooks`, `rai_pre_hooks`, `rai_post_hooks`. - -## CLI API - -- `oris run [--input-json '{"key":"value"}']` -- `oris validate ` - -CLI output format: - -- `run`: JSON run summary to stdout (`run_id`, `status`, `output`, `trace`; see `PipelineResult.to_run_summary()`) -- `validate`: human-readable success message - -## Versioning Policy - -Oris follows semantic versioning: - -- major: breaking public API changes -- minor: backward-compatible features -- patch: backward-compatible fixes - -Public API changes require updates to this document and release notes. diff --git a/README.md b/README.md index 29594ee..a3df039 100644 --- a/README.md +++ b/README.md @@ -1,152 +1,214 @@ -
- Oris — Responsible AI pipeline runtime for Python - -| | | -| :--- | :--- | -| **CI** | [![CI](https://github.com/DevStrikerTech/oris/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/DevStrikerTech/oris/actions/workflows/ci.yml) [![types - Mypy](https://img.shields.io/badge/types-Mypy-blue.svg)](https://github.com/python/mypy) [![Coverage](https://img.shields.io/badge/coverage-%E2%89%A584%25-brightgreen.svg)](#development-quality-gates) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) | -| **Docs** | [![Documentation](https://img.shields.io/website?label=documentation&up_message=online&url=https%3A%2F%2Fdevstrikertech.github.io%2Foris%2F)](https://devstrikertech.github.io/oris/) [![Oris docs](https://github.com/DevStrikerTech/oris/actions/workflows/oris-docs.yml/badge.svg?branch=prod)](https://github.com/DevStrikerTech/oris/actions/workflows/oris-docs.yml) | +

+ + Oris — Responsible AI pipeline runtime for Python + +

+ +| | | +| ------- | --- | +| **CI** | [![CI](https://github.com/DevStrikerTech/oris/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/DevStrikerTech/oris/actions/workflows/ci.yml) [![types - Mypy](https://img.shields.io/badge/types-Mypy-blue.svg)](https://github.com/python/mypy) [![Coverage](https://img.shields.io/badge/coverage-%E2%89%A584%25-brightgreen.svg)](CONTRIBUTING.md#quality-gates) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) | +| **Docs** | [![Website](https://img.shields.io/website?label=documentation&up_message=online&url=https%3A%2F%2Fdevstrikertech.github.io%2Foris%2F)](https://devstrikertech.github.io/oris/) [![Docs](https://github.com/DevStrikerTech/oris/actions/workflows/docs.yml/badge.svg?branch=prod)](https://github.com/DevStrikerTech/oris/actions/workflows/docs.yml) | | **Package** | [![PyPI](https://img.shields.io/pypi/v/oris-ai)](https://pypi.org/project/oris-ai/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/oris-ai?logo=python&logoColor=gold)](https://pypi.org/project/oris-ai/) [![License](https://img.shields.io/github/license/DevStrikerTech/oris)](https://github.com/DevStrikerTech/oris/blob/prod/LICENSE) | -| **Meta** | [![Issues](https://img.shields.io/github/issues/DevStrikerTech/oris)](https://github.com/DevStrikerTech/oris/issues) [![Repository](https://img.shields.io/badge/GitHub-DevStrikerTech%2Foris-181717?logo=github)](https://github.com/DevStrikerTech/oris) | -
+| **Meta** | [![Issues](https://img.shields.io/github/issues/DevStrikerTech/oris)](https://github.com/DevStrikerTech/oris/issues) [![GitHub](https://img.shields.io/badge/GitHub-DevStrikerTech%2Foris-181717?logo=github)](https://github.com/DevStrikerTech/oris) | -**Oris** is an open-source **Responsible AI** runtime for Python: define pipelines in YAML or in code, run them through a framework-agnostic executor, and keep **safety, observability, and validation** on by default. +[Oris](https://devstrikertech.github.io/oris/) is an open-source **Responsible AI** runtime for Python. Describe **pipelines** in YAML (or build them in code), run them through one **executor**, and get **input/output policy checks** and **run- and step-level traces** by default. -Design sequential pipelines with pluggable components, provider backends, and guardrails on inputs and outputs. Use the CLI for validate/run flows, or embed **Oris** in your own services with typed APIs and run- and step-level tracing for debugging and governance. +Oris stays **framework-agnostic**: anything you can invoke like `run(dict)` can use the same boundaries—including external LLM stacks wrapped with **SafeRunner**—so you can experiment locally and ship with clearer safety and observability defaults. ## Table of contents -- [Why Oris](#why-oris) -- [Features](#features) - [Installation](#installation) +- [Documentation](#documentation) +- [Features](#features) - [Quick start](#quick-start) -- [Documentation (web)](#documentation-web) +- [CLI](#cli) +- [Output format](#output-format) +- [SafeRunner](#saferunner) - [Project layout](#project-layout) -- [Development quality gates](#development-quality-gates) -- [Governance and standards](#governance-and-standards) +- [Examples and notebooks](#examples-and-notebooks) +- [Contributing](#contributing) - [License](#license) -## Why Oris - -- **Responsible by default**: pipeline runs pass through input and output guards. -- **Framework-agnostic runtime**: integrate with any object that exposes a `run(...)`-style entry point. -- **Production-oriented architecture**: typed interfaces, clear module boundaries, strict CI quality gates. -- **Traceable execution**: run-level and step-level traces for debugging and governance workflows. +## Installation -## Features +The simplest way to get Oris is via pip: -**YAML or embedded pipelines** -Describe components and wiring in YAML, or build pipelines in Python with the same registry and executor. +```bash +pip install oris-ai +``` -**Guards and policy** -Basic policy hooks for harmful or sensitive inputs and outputs, with audit logging and redaction of sensitive fields. +Verify the CLI: -**Extensible components** -Registry-based components, provider abstraction for model backends, and a CLI for `validate` and `run`. +```bash +oris --help +``` -**Observability** -Structured traces across the run so you can reason about what executed and when. +**From source** (library, CLI, and tests): -## Installation +```bash +git clone https://github.com/DevStrikerTech/oris.git +cd oris +python -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e . +``` -For library and CLI use from [PyPI](https://pypi.org/project/oris-ai/): +**Developers** (lint, types, tests, notebook execution): ```bash -pip install oris-ai +pip install -e ".[dev]" ``` -For contributing or running tests from a clone: +Oris requires **Python 3.10+**. Runtime dependencies are minimal (**PyYAML** only). For a fuller walkthrough, see the [Installation](https://devstrikertech.github.io/oris/get-started/installation/) page in the docs. + +## Documentation + +If you are new to the project, start with the [**Introduction**](https://devstrikertech.github.io/oris/), then follow [**Installation**](https://devstrikertech.github.io/oris/get-started/installation/) and [**Quickstart**](https://devstrikertech.github.io/oris/get-started/quickstart/) on the documentation site. The [**Concepts**](https://devstrikertech.github.io/oris/concepts/overview/) section explains pipelines, components, providers, RAI, and traces; [**Guides**](https://devstrikertech.github.io/oris/guides/cli/) cover the CLI, SafeRunner, and run summaries. + +**Site:** [devstrikertech.github.io/oris](https://devstrikertech.github.io/oris/) (MkDocs Material, similar information architecture to projects like [Haystack](https://docs.haystack.deepset.ai/docs/intro)). + +**Preview locally:** ```bash -python -m venv .venv -source .venv/bin/activate # Windows: .venv\Scripts\activate -pip install -e ".[dev]" +pip install -e ".[docs]" +mkdocs serve ``` +The **Docs** workflow publishes to GitHub Pages on pushes to **`prod`** ([`.github/workflows/docs.yml`](.github/workflows/docs.yml)). In **Settings → Pages**, choose **GitHub Actions** as the source if needed. + +## Features + +**YAML-first pipelines** +Define `steps`, optional `providers`, and `settings` (such as tracing). Configuration is validated before execution; YAML is loaded with `yaml.safe_load` only. + +**Guards and policy** +A default `PolicyEnforcer` applies input checks (blocked keys, basic injection heuristics, simple PII-shaped patterns) and output checks (blocked terms and test-oriented stubs). The same policy surface is used by **SafeRunner** for external callables. + +**Built-in components and provider stubs** +Use `passthrough`, `template_response`, and `generate` / `llm_echo` from the default registry. Declared `openai` / `huggingface` provider types are **stubs** (no network I/O in the core package) so CI and demos stay reproducible. + +**Observability** +Each run produces a `RunTrace` with per-step latency, status, and flags. `PipelineResult.to_run_summary()` gives a stable JSON-oriented shape for logs and the CLI (with optional redaction of sensitive-looking keys). + +**CLI parity** +`oris validate` and `oris run` use the same definitions as `Pipeline.from_yaml` in Python, with `--format pretty` and `--debug` for human-friendly output and stderr trace lines. + ## Quick start -Save the following as `pipeline.yaml` (any path you prefer): +Save as `pipeline.yaml`: ```yaml -name: basic_pipeline -components: - - type: passthrough - name: normalize_input - - type: template_response - name: responder +name: quickstart +settings: + tracing: true +steps: + - id: reply + type: template_response config: - template: "AI answer placeholder for: {query}" + template: "Answer placeholder for: {query}" ``` -Python (from the same directory as the file): +**Python** ```python from oris import Pipeline pipeline = Pipeline.from_yaml("pipeline.yaml") - -result = pipeline.run({ - "query": "What is AI?" -}) +result = pipeline.run({"query": "What is responsible AI?"}) print(result.output) ``` -CLI: +**CLI** ```bash oris validate pipeline.yaml -oris run pipeline.yaml --input-json '{"query":"What is AI?"}' +oris run pipeline.yaml --input-json '{"query":"What is responsible AI?"}' +oris run pipeline.yaml --input-json '{"query":"hi"}' --format pretty --debug ``` -## Documentation (web) +`--debug` prints trace-oriented details on **stderr**; stdout remains the JSON summary. Sample YAML and notebooks live under [`examples/`](examples/). -The full site (architecture, public API, security, releases, contributing) is published with **MkDocs Material**: **[devstrikertech.github.io/oris](https://devstrikertech.github.io/oris/)**. +## CLI -Private repositories need a GitHub plan that includes **GitHub Pages**; in repo settings choose **Pages → Build and deployment → GitHub Actions** after the first successful deploy from `prod`. +| Command | Purpose | +| :--- | :--- | +| `oris validate ` | Load and validate the pipeline (schema, components, providers). | +| `oris run --input-json ''` | Run with a JSON object as input; default stdout is a compact JSON summary. | +| `oris run ... --format pretty` | Pretty-printed JSON summary. | +| `oris run ... --debug` | Stderr: `run_id`, trace status, per-step latency and flags. | +| `oris validate ... --debug` | Stderr: pipeline name and step list. | -Local preview: +## Output format -```bash -pip install -e ".[dev]" -bash scripts/sync_doc_sources.sh && mkdocs serve +`Pipeline.run` returns a **`PipelineResult`**: `output` (dict), `trace` (`RunTrace`), and `metadata`. See [`models.py`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/runtime/models.py). + +`result.to_run_summary()` includes: + +- **`run_id`** — Run identifier. +- **`status`** — `"success"` or `"failed"` from trace status. +- **`output`** — Final payload (CLI may redact nested sensitive-looking keys). +- **`trace`** — Per-step entries: `step_id`, `component_name`, `status`, `latency_ms`, `flags`. + +CLI formatting and redaction: [`output.py`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/cli/output.py). + +## SafeRunner + +**[`SafeRunner`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/integrations/safe_runner.py)** wraps external inference or tools with the same **`PolicyEnforcer`** as the main executor—validate input, run a callable or `run(dict)` target, validate output, optionally attach a one-step trace. + +```python +from oris.integrations import SafeRunner +from oris.rai.policy import PolicyEnforcer + +def my_external_llm(payload: dict) -> dict: + q = payload.get("query", "") + return {"output": f"stub response for: {q!r}"} + +runner = SafeRunner(my_external_llm, policy=PolicyEnforcer()) + +plain = runner.run({"query": "Hello"}) +traced = runner.run({"query": "Hello"}, include_trace=True) ``` +Returns are normalized to `dict` from mappings or Pydantic-style `model_dump()`. More detail: [SafeRunner guide](https://devstrikertech.github.io/oris/guides/safe-runner/). + ## Project layout -```text -oris/ -├── src/oris/ -│ ├── core/ -│ ├── runtime/ -│ ├── components/ -│ ├── providers/ -│ ├── rai/ -│ ├── integrations/ -│ ├── tracing/ -│ ├── cli/ -│ └── api/ -├── tests/ -├── docs/ -└── .github/workflows/ -``` +| Area | Role | +| :--- | :--- | +| `oris.core` | Shared enums and exceptions. | +| `oris.components` | Component registry and built-ins. | +| `oris.pipeline` | YAML loading, schema, plan, builder. | +| `oris.runtime` | Executor, orchestrator, hooks, trace manager, `PipelineResult`. | +| `oris.rai` | `PolicyEnforcer`, input/output guards. | +| `oris.providers` | `LLMProvider` and built-in YAML provider stubs. | +| `oris.integrations` | `SafeRunner`. | +| `oris.tracing` | Run and step trace models. | +| `oris.cli` | `oris` CLI entrypoint. | + +## Examples and notebooks + +| Asset | Description | +| :--- | :--- | +| [`examples/simple_generation.yaml`](examples/simple_generation.yaml) | Template step; good first `Pipeline.run`. | +| [`examples/provider_pipeline.yaml`](examples/provider_pipeline.yaml) | Provider declaration + `generate` (needs `OPENAI_API_KEY` for the stub). | +| [`examples/basic_pipeline.ipynb`](examples/basic_pipeline.ipynb) | YAML → run → `to_run_summary()`. | +| [`examples/safe_runner.ipynb`](examples/safe_runner.ipynb) | SafeRunner, traces, policy violations. | +| [`examples/llm_integration.ipynb`](examples/llm_integration.ipynb) | Optional Ollama + mock fallback; YAML provider stub. | -## Development quality gates +Execute notebooks from the repo root (after `pip install -e ".[dev]"`): + +```bash +export JUPYTER_CONFIG_DIR="$PWD/.jupyter" && mkdir -p .jupyter +python -m nbconvert --to notebook --execute examples/basic_pipeline.ipynb --inplace +``` -- `ruff` for linting and format checks -- `mypy` in strict mode -- `pytest` and `pytest-cov` with a minimum of **84%** coverage -- `pre-commit` hooks for local checks +Repeat for the other notebooks, or open them in your editor. -## Governance and standards +## Contributing -- Engineering standards: `ENGINEERING_STANDARDS.md` -- Security policy: `SECURITY.md` -- Architecture notes: `ARCHITECTURE.md` -- Public API guarantees: `PUBLIC_API.md` -- Provider system design: `PROVIDER_DESIGN.md` -- Release process: `RELEASE.md` -- Contribution process: `CONTRIBUTING.md` +We welcome issues and pull requests. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md) (branches, quality gates, tests). Report security issues per [`SECURITY.md`](SECURITY.md). Community expectations: [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). ## License -MIT — see [`LICENSE`](https://github.com/DevStrikerTech/oris/blob/prod/LICENSE) in the repository root. +MIT — see [`LICENSE`](LICENSE). diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index e79f4ec..0000000 --- a/RELEASE.md +++ /dev/null @@ -1,43 +0,0 @@ -# Release Process - -## Branch Model - -- `prod`: production-ready code only. -- `dev`: integration branch for validated work. -- `feat/*`: feature development branches. - -## Merge Rules - -- No direct commits to `dev` or `prod`. -- Every merge requires an approved PR. -- CI checks are mandatory before merge. -- `dev` is merged to `prod` daily after release verification. - -## Pre-Release Checklist - -- [ ] all CI checks pass on `dev` -- [ ] coverage remains >= 84% -- [ ] release notes drafted -- [ ] security-sensitive changes reviewed -- [ ] public API change log updated - -## Versioning - -- bump versions in `pyproject.toml` -- tag release as `vX.Y.Z` -- generate changelog section for version - -## Distribution (pip) and the full chain - -**Flow:** `feat/*` → PR → **`dev`** (CI) → merge **`dev` → `prod`** (CI) → **tag `vX.Y.Z` on the `prod` commit** → **Publish** workflow → PyPI. - -Consumers install with **`pip install oris-ai`** (after a release is on PyPI). - -### GitHub Actions - -- **CI** (`.github/workflows/ci.yml`): PRs to `dev`, pushes to `dev` / `prod`. -- **Publish** (`.github/workflows/publish.yml`): - - **Tag `v*`** (e.g. `v0.1.0`): build, `twine check`, upload to **PyPI** using secret **`ORIS_PYPI_TOKEN`**. - - **Actions → Publish → Run workflow**: choose **testpypi** (needs **`ORIS_TEST_PYPI_TOKEN`** from [test.pypi.org](https://test.pypi.org)) or **pypi** to exercise uploads without tagging. - -Only tag release commits on **`prod`**. Bump **`version`** in `pyproject.toml` before tagging. diff --git a/SECURITY.md b/SECURITY.md index 94f37b0..605a71f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,45 +1,44 @@ # Security Policy -## Security Posture +## Supported versions -Oris is a Responsible AI runtime with security controls embedded in execution and engineering workflow. +Security fixes are applied to the **latest released version** on PyPI (`oris-ai`) and the maintained development branches in this repository. Runtime support follows **`requires-python >= 3.10`** in `pyproject.toml` (currently Python **3.10–3.13** in CI). -## Core Security Requirements +Use an up-to-date patch release of Python and of this package where possible. -- No hardcoded credentials, secrets, or API keys. -- Credentials must come from environment variables or managed secret stores. -- All YAML/config inputs must be validated prior to execution. -- Unsafe YAML loaders are prohibited. -- Sensitive values must never be logged in clear text. +## Reporting a vulnerability -## Runtime Controls +**Please do not file public issues for undisclosed security vulnerabilities.** That helps avoid tipping off attackers before a fix is available. -- Input guard blocks prohibited key patterns (e.g., `password`, `token`, `secret`). -- Output guard blocks configured harmful term classes. -- Audit logger redacts sensitive fields before writing logs. -- Execution exceptions are wrapped in framework-specific error types for consistent handling. +Preferred options: -## Secure Development Lifecycle +1. **[GitHub Security Advisories](https://github.com/DevStrikerTech/oris/security/advisories/new)** — private report to maintainers (recommended). +2. **Email** — if you cannot use GitHub: contact the maintainers at a published project security address if one is listed in the repository or org profile; include the details below. -- pre-commit hooks include linting, type checks, tests, and secret scanning. -- GitHub CI enforces quality checks and coverage gates. -- PR review is mandatory for all merges into protected branches. +Include as much as you can: -## Dependency Hygiene +- **Affected versions** (package version, commit, or branch) +- **Reproduction steps** and minimal proof of concept if safe to share +- **Impact** (confidentiality, integrity, availability, supply chain, etc.) +- **Suggested mitigation** (optional) -- Keep dependencies minimal and pinned with lower bounds. -- Run dependency scanning in CI (future enhancement: `pip-audit`/`safety`). -- Remove unused dependencies promptly. +Maintainers will acknowledge receipt as soon as practical and work with you on a disclosure timeline (coordinated disclosure when applicable). -## Vulnerability Reporting +## Secret and credential handling -Please do not open public issues for security vulnerabilities. +- **Never** hardcode API keys, tokens, passwords, or private keys in source, tests, notebooks, or examples committed to the repo. +- Load credentials from **environment variables** or your organization’s **secret manager**. Built-in provider YAML uses `api_key_env` (or equivalent) to name the variable—never the secret value. +- **Do not** log secrets in clear text. The CLI may redact values for sensitive-looking keys in summaries; treat logs and traces as sensitive in production. +- YAML is parsed with **`yaml.safe_load` only**. Do not introduce unsafe loaders or arbitrary object construction from config. -- Email: `security@oris-ai.org` (placeholder) -- Include: - - affected version - - reproduction steps - - impact assessment - - optional mitigation suggestion +## Secure development practices -Maintainers will acknowledge within 72 hours and provide coordinated disclosure timelines. +- **pre-commit** includes linting, typing, tests, and secret scanning (see `.pre-commit-config.yaml` and `.secrets.baseline`). +- **CI** runs the same quality checks as local development (see `.github/workflows/ci.yml`). +- **Dependencies:** keep the dependency set small; review upgrades and lockfiles in PRs. + +## Runtime controls (overview) + +Oris applies default **input/output policy** checks (e.g. blocked keys and terms, basic injection heuristics, simple PII-style patterns). These are **not** a substitute for full product security review, sandboxing, or enterprise policy engines—layer defenses appropriate to your threat model. + +For dependency or supply-chain issues in **third-party** libraries, report them to the upstream project where appropriate, and upgrade Oris’s declared minimums when a fix is available. diff --git a/docs/concepts/components.md b/docs/concepts/components.md new file mode 100644 index 0000000..35f832a --- /dev/null +++ b/docs/concepts/components.md @@ -0,0 +1,40 @@ +# Components & providers + +## Components + +Components are registered callables/classes keyed by a **type string** (e.g. `passthrough`, `template_response`, `llm_echo`). The default registry is created by [`create_builtin_registry()`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/components/builtin.py). + +Built-in types include: + +| Type | Purpose | +| :--- | :--- | +| `passthrough` | Passes the payload through unchanged (useful for wiring tests). | +| `template_response` | Formats `output` from a template using the `query` field. | +| `llm_echo` / `generate` | Invokes an injected **`LLMProvider`** (alias `generate` → `llm_echo`). | + +Custom components can be registered on a **`ComponentRegistry`** for advanced use cases; keep public surfaces minimal unless you intend to support them long term. + +## Providers + +**Providers** implement the abstract [`LLMProvider`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/providers/base.py) contract (`generate(prompt) -> str`). YAML declares them under `providers:` with a **`type`** (`openai`, `huggingface`, …) and configuration such as **`model`** and **`api_key_env`**. + +The built-in **`openai`** and **`huggingface`** implementations are **stubs** (no real network I/O in the default package): they validate that the named environment variable is set and return deterministic placeholder text. That keeps CI and notebooks reproducible while you swap in real clients in your own codebase or wrap calls with [**SafeRunner**](../guides/safe-runner.md). + +Example sketch: + +```yaml +providers: + openai: + type: openai + model: gpt-4 + api_key_env: OPENAI_API_KEY +steps: + - id: step1 + type: generate + provider: openai +``` + +## Next + +- [**RAI & observability**](rai-tracing.md) — how guards relate to execution +- [**Runs, output & traces**](../guides/output.md) — what you get from a run diff --git a/docs/concepts/overview.md b/docs/concepts/overview.md new file mode 100644 index 0000000..780ce4a --- /dev/null +++ b/docs/concepts/overview.md @@ -0,0 +1,29 @@ +# Concepts overview + +Oris is a **pipeline runtime**: you declare **what** runs (steps, components, optional providers), and the framework handles **validation**, **policy hooks**, **execution order**, and **tracing**. + +## Main ideas + +| Concept | Role | +| :--- | :--- | +| **Pipeline** | A validated **execution plan** built from YAML or an in-memory config. Entry point: [`Pipeline`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/pipeline/pipeline.py). | +| **Step** | One unit of work: a **component type**, optional **provider** reference, and **config**. | +| **Component** | Registered Python type that implements `run(data, context)` (e.g. `template_response`, `llm_echo`). | +| **Provider** | Backend for LLM-style components; declared under `providers:` in YAML and injected where `provider:` is set. | +| **Policy (RAI)** | Shared **`PolicyEnforcer`** checks on pipeline input/output (and via **`SafeRunner`** for external callables). | +| **Trace** | **`RunTrace`** with **`StepTrace`** entries: latency, status, flags—serialized in **`to_run_summary()`**. | + +## Execution flow (high level) + +1. **Load** YAML with safe parsing; **validate** schema and declarations. +2. **Build** components from the registry; resolve **providers** where needed. +3. **Execute** via **`RuntimeExecutor`**: pipeline pre-hooks (including input policy), each step (with step traces), pipeline post-hooks (including output policy). +4. **Return** **`PipelineResult`** (`output`, `trace`, `metadata`). + +For deeper internals, browse the source under `src/oris/` on GitHub—start with [`runtime/executor.py`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/runtime/executor.py). + +## Where to go next + +- [**Pipelines & YAML**](pipelines.md) — document shape and settings +- [**Components & providers**](components.md) — built-ins and extension points +- [**RAI & observability**](rai-tracing.md) — guards and traces diff --git a/docs/concepts/pipelines.md b/docs/concepts/pipelines.md new file mode 100644 index 0000000..2d83f33 --- /dev/null +++ b/docs/concepts/pipelines.md @@ -0,0 +1,46 @@ +# Pipelines & YAML + +Pipelines are described as YAML documents (or equivalent dicts via `Pipeline.from_config`). The schema enforces allowed keys and step shape before anything runs. + +## Top-level keys + +| Key | Purpose | +| :--- | :--- | +| `name` | Logical pipeline name (metadata). | +| `settings` | e.g. `device`, `tracing` (boolean). | +| `metadata` | Optional arbitrary metadata block. | +| `providers` | Named provider declarations for LLM-backed steps. | +| `steps` | Ordered list of steps (**canonical** format). | +| `components` | Legacy list form; prefer `steps` for new work. | + +## Canonical `steps` item + +Each step typically includes: + +- **`id`** — Stable identifier for traces and debugging. +- **`type`** — Component type key in the registry (e.g. `template_response`, `generate`). +- **`provider`** — Logical provider id when the component needs a backend. +- **`config`** — Component-specific configuration (validated per component). + +Example: + +```yaml +name: demo +settings: + tracing: true +steps: + - id: answer + type: template_response + config: + template: "Response: {query}" +``` + +## Safe loading + +Configs are loaded with **`yaml.safe_load`** only—no arbitrary Python objects from YAML. + +## Related + +- [**Quickstart**](../get-started/quickstart.md) — minimal runnable file +- [**Components & providers**](components.md) — how `type` and `provider` resolve +- [**CLI reference**](../guides/cli.md) — `oris validate` / `oris run` diff --git a/docs/concepts/rai-tracing.md b/docs/concepts/rai-tracing.md new file mode 100644 index 0000000..b659fd8 --- /dev/null +++ b/docs/concepts/rai-tracing.md @@ -0,0 +1,29 @@ +# RAI & observability + +## Responsible AI (RAI) policy + +Oris applies a default [**`PolicyEnforcer`**](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/rai/policy.py) at pipeline boundaries: + +- **Input checks** — Blocked keys (e.g. `password`, `secret`, `token`), basic prompt-injection heuristics, and simple PII-shaped patterns in string values. +- **Output checks** — Blocked terms, plus stub hooks for toxicity/hallucination markers used in tests. + +These checks align with **`InputGuard`** / **`OutputGuard`** hooks in the executor so behavior stays consistent whether you run a full pipeline or [**SafeRunner**](../guides/safe-runner.md). + +!!! warning "Layer your defenses" + + Default policy is a **baseline**, not a complete safety program. Combine Oris with org policies, model safeguards, and human review appropriate to your domain. + +## Tracing + +Each run produces a **`RunTrace`** with ordered **`StepTrace`** records: timestamps, status, per-step **`latency_ms`**, **`flags`**, and optional **`metadata`**. + +The stable JSON-oriented view is **`PipelineResult.to_run_summary()`**—used by the CLI and ideal for logs. See [**Runs, output & traces**](../guides/output.md). + +## Audit logging + +The tracing package includes audit helpers for redaction-aware logging—see [`oris.tracing.audit`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/tracing/audit.py) in the repository. + +## Related + +- [**CLI reference**](../guides/cli.md) — `--debug` trace lines on stderr +- [**Security**](../development/security.md) — reporting issues and secret handling diff --git a/docs/development/code-of-conduct.md b/docs/development/code-of-conduct.md new file mode 100644 index 0000000..bae50c0 --- /dev/null +++ b/docs/development/code-of-conduct.md @@ -0,0 +1,7 @@ +# Code of Conduct + +Oris follows the contributor **Code of Conduct** checked into the repository. + +**Read the full text:** [`CODE_OF_CONDUCT.md` on GitHub](https://github.com/DevStrikerTech/oris/blob/dev/CODE_OF_CONDUCT.md) + +For questions or reports, use the contact process described in that file. diff --git a/docs/development/contributing.md b/docs/development/contributing.md new file mode 100644 index 0000000..241dfef --- /dev/null +++ b/docs/development/contributing.md @@ -0,0 +1,32 @@ +# Contributing + +Thank you for helping improve Oris. + +## Quick links + +- **Full contributor guide (source of truth):** [`CONTRIBUTING.md` on GitHub](https://github.com/DevStrikerTech/oris/blob/dev/CONTRIBUTING.md) +- **Issues & PRs:** [github.com/DevStrikerTech/oris](https://github.com/DevStrikerTech/oris) +- **PR template:** [`.github/PULL_REQUEST_TEMPLATE.md`](https://github.com/DevStrikerTech/oris/blob/dev/.github/PULL_REQUEST_TEMPLATE.md) + +## At a glance + +1. **Fork / clone** and create a branch from **`dev`** (`feat/...` or `fix/...`). +2. **Install** with `pip install -e ".[dev]"` and optional `pre-commit install`. +3. **Implement** with tests; keep coverage **≥ 84%** (enforced in `pyproject.toml`). +4. **Run** `ruff check`, `ruff format --check`, `mypy`, and `pytest` before opening a PR. +5. **Open a PR** targeting **`dev`**. + +## Documentation + +To preview this site locally: + +```bash +pip install -e ".[docs]" +mkdocs serve +``` + +Edits live under the repository’s **`docs/`** directory (Markdown + `mkdocs.yml`). + +## Code of Conduct + +Participation is governed by the [**Code of Conduct**](code-of-conduct.md). diff --git a/docs/development/security.md b/docs/development/security.md new file mode 100644 index 0000000..334db3b --- /dev/null +++ b/docs/development/security.md @@ -0,0 +1,20 @@ +# Security + +## Reporting vulnerabilities + +Please **do not** open public issues for undisclosed security vulnerabilities. + +**Preferred:** use [GitHub **Security advisories**](https://github.com/DevStrikerTech/oris/security/advisories) for a private report. + +**Full policy:** [`SECURITY.md` on GitHub](https://github.com/DevStrikerTech/oris/blob/dev/SECURITY.md) (supported versions, secret handling, coordinated disclosure expectations). + +## Guidelines (summary) + +- Never commit **secrets**; use environment variables or a secret manager. +- Provider YAML uses **`api_key_env`** to name variables—never embed key material. +- Configuration is loaded with **`yaml.safe_load`** only. + +## Next + +- [**Contributing**](contributing.md) +- [**Code of Conduct**](code-of-conduct.md) diff --git a/docs/examples/index.md b/docs/examples/index.md new file mode 100644 index 0000000..79422b5 --- /dev/null +++ b/docs/examples/index.md @@ -0,0 +1,35 @@ +# Examples + +The **Oris** repository ships runnable **YAML** samples and **Jupyter notebooks** under the [`examples/`](https://github.com/DevStrikerTech/oris/tree/dev/examples) directory on GitHub. + +## YAML files + +| File | Description | +| :--- | :--- | +| [`simple_generation.yaml`](https://github.com/DevStrikerTech/oris/blob/dev/examples/simple_generation.yaml) | Template response step; good default for first `Pipeline.run`. | +| [`provider_pipeline.yaml`](https://github.com/DevStrikerTech/oris/blob/dev/examples/provider_pipeline.yaml) | Declares an `openai` provider stub and a `generate` step (requires `OPENAI_API_KEY` in the environment). | + +## Notebooks + +| Notebook | Topics | +| :--- | :--- | +| [`basic_pipeline.ipynb`](https://github.com/DevStrikerTech/oris/blob/dev/examples/basic_pipeline.ipynb) | Load YAML, run in Python, print `to_run_summary()`. | +| [`safe_runner.ipynb`](https://github.com/DevStrikerTech/oris/blob/dev/examples/safe_runner.ipynb) | `SafeRunner`, traces, policy violations. | +| [`llm_integration.ipynb`](https://github.com/DevStrikerTech/oris/blob/dev/examples/llm_integration.ipynb) | Optional **Ollama** HTTP call with mock fallback; YAML provider stub. | + +## Run notebooks locally + +From a clone, with dev dependencies: + +```bash +pip install -e ".[dev]" +export JUPYTER_CONFIG_DIR="$PWD/.jupyter" && mkdir -p .jupyter +python -m nbconvert --to notebook --execute examples/basic_pipeline.ipynb --inplace +``` + +Repeat for other notebooks, or open them in **VS Code**, **Cursor**, or **JupyterLab** (install a Jupyter frontend separately if needed). + +## Next + +- [**Quickstart**](../get-started/quickstart.md) +- [**SafeRunner**](../guides/safe-runner.md) diff --git a/docs/get-started/installation.md b/docs/get-started/installation.md new file mode 100644 index 0000000..9bc829b --- /dev/null +++ b/docs/get-started/installation.md @@ -0,0 +1,47 @@ +# Installation + +Oris is published on PyPI as **`oris-ai`**. Runtime dependencies are intentionally small (**Python 3.10+** and **PyYAML**). + +## From PyPI + +```bash +pip install oris-ai +``` + +Verify the CLI: + +```bash +oris --help +``` + +## Editable install (from source) + +Clone the repository and install in editable mode for development or examples: + +```bash +git clone https://github.com/DevStrikerTech/oris.git +cd oris +python -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e . +``` + +## Optional extras + +| Extra | Purpose | +| :--- | :--- | +| `pip install -e ".[dev]"` | Ruff, Mypy, pytest, pre-commit, notebook execution (`nbconvert`, `ipykernel`) | +| `pip install -e ".[docs]"` | MkDocs Material (build this documentation site locally) | + +Build docs locally: + +```bash +pip install -e ".[docs]" +mkdocs serve +``` + +Open the URL shown in the terminal (usually `http://127.0.0.1:8000`). + +## Next + +Continue to [**Quickstart**](quickstart.md) to run your first pipeline. diff --git a/docs/get-started/quickstart.md b/docs/get-started/quickstart.md new file mode 100644 index 0000000..154f277 --- /dev/null +++ b/docs/get-started/quickstart.md @@ -0,0 +1,66 @@ +# Quickstart + +This page walks through a **minimal YAML pipeline**, running it from **Python** and the **CLI**. + +## 1. Create a pipeline file + +Save as `pipeline.yaml`: + +```yaml +name: quickstart +settings: + tracing: true +steps: + - id: reply + type: template_response + config: + template: "Answer placeholder for: {query}" +``` + +The `template_response` component fills an `output` field from the `{query}` placeholder in your input payload. + +## 2. Run from Python + +```python +from oris import Pipeline + +pipeline = Pipeline.from_yaml("pipeline.yaml") +result = pipeline.run({"query": "What is responsible AI?"}) + +print(result.output) +``` + +You should see a dict that includes your original `query` and a string `output` from the template. + +## 3. Run from the CLI + +```bash +oris validate pipeline.yaml +oris run pipeline.yaml --input-json '{"query":"What is responsible AI?"}' +``` + +Pretty-printed JSON and debug-style trace lines on stderr: + +```bash +oris run pipeline.yaml --input-json '{"query":"hi"}' --format pretty --debug +``` + +## 4. Inspect structured output + +```python +summary = result.to_run_summary() +print(summary["status"], summary["run_id"]) +print(summary["trace"]) +``` + +See [**Runs, output & traces**](../guides/output.md) for the full summary shape. + +## Sample files in the repo + +The repository includes ready-made YAML under [`examples/`](https://github.com/DevStrikerTech/oris/tree/dev/examples) and Jupyter notebooks you can execute locally—see [**Examples**](../examples/index.md). + +## Next + +- [**Concepts overview**](../concepts/overview.md) — how pieces fit together +- [**CLI reference**](../guides/cli.md) — all `oris` subcommands +- [**SafeRunner**](../guides/safe-runner.md) — wrap external LLM code with the same policy checks diff --git a/docs/guides/cli.md b/docs/guides/cli.md new file mode 100644 index 0000000..614fa9b --- /dev/null +++ b/docs/guides/cli.md @@ -0,0 +1,50 @@ +# CLI reference + +The **`oris`** command ships with the `oris-ai` package (`[project.scripts]` → `oris.cli.main:main`). + +## Global usage + +```bash +oris --help +``` + +Subcommands are required: **`validate`** or **`run`**. + +## `oris validate` + +Load and validate a pipeline YAML file (schema, components, providers). Exits `0` when valid. + +```bash +oris validate path/to/pipeline.yaml +``` + +**`--debug`** — Prints a short plan summary to **stderr** (pipeline name, step ids and component names). Stdout remains a single success line. + +## `oris run` + +Execute a pipeline with a JSON object as input. + +```bash +oris run path/to/pipeline.yaml --input-json '{"query":"Hello"}' +``` + +| Flag | Effect | +| :--- | :--- | +| `--input-json` | JSON **object** only (default `{}`). | +| `--format pretty` | Pretty-printed JSON summary on stdout. | +| `--debug` | Print run id, trace status, and per-step latency/flags to **stderr**; stdout unchanged. | + +### Exit codes + +- **`0`** — Success. +- **`1`** — Expected Oris errors (configuration, validation, guard violations, etc.). +- **`2`** — Argument/parser issues (rare in normal use). + +## Output shape + +Stdout is a JSON object derived from **`PipelineResult.to_run_summary()`**, with optional **redaction** of sensitive-looking keys in nested mappings. See [**Runs, output & traces**](output.md). + +## Related + +- [**Quickstart**](../get-started/quickstart.md) +- [**Pipelines & YAML**](../concepts/pipelines.md) diff --git a/docs/guides/output.md b/docs/guides/output.md new file mode 100644 index 0000000..cd2e160 --- /dev/null +++ b/docs/guides/output.md @@ -0,0 +1,33 @@ +# Runs, output & traces + +## `PipelineResult` + +`Pipeline.run(...)` returns a **`PipelineResult`**: + +| Field | Meaning | +| :--- | :--- | +| **`output`** | Final `dict` payload from the pipeline. | +| **`trace`** | **`RunTrace`**: run id, status, timestamps, list of **`StepTrace`**. | +| **`metadata`** | Small run-level bag (e.g. source hints for integrations). | + +Source: [`oris.runtime.models`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/runtime/models.py). + +## `to_run_summary()` + +Calling **`result.to_run_summary()`** returns a JSON-friendly **`dict`**: + +- **`run_id`** — String identifier for the run. +- **`status`** — `"success"` or `"failed"` derived from trace status. +- **`output`** — Copy of the output mapping. +- **`trace`** — List of per-step summaries: `step_id`, `component_name`, `status`, `latency_ms`, `flags`, etc. + +This is the shape the **CLI** prints (with optional **redaction** of sensitive-looking keys). Redaction logic lives in [`oris.cli.output`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/cli/output.py). + +## Debug-style CLI output + +`oris run ... --debug` mirrors trace information on **stderr** (run id, each step’s latency and flags) while keeping the JSON summary on **stdout**—handy for local troubleshooting without changing structured log consumers. + +## Related + +- [**CLI reference**](cli.md) +- [**Concepts overview**](../concepts/overview.md) diff --git a/docs/guides/safe-runner.md b/docs/guides/safe-runner.md new file mode 100644 index 0000000..23e9d99 --- /dev/null +++ b/docs/guides/safe-runner.md @@ -0,0 +1,54 @@ +# SafeRunner + +**`SafeRunner`** wraps **external** inference or tool code so it passes through the same **`PolicyEnforcer`** entry points as the main **`RuntimeExecutor`**—input validation before the call, output validation after, with optional tracing. + +Implementation: [`oris.integrations.safe_runner`](https://github.com/DevStrikerTech/oris/blob/dev/src/oris/integrations/safe_runner.py). + +## When to use it + +- You call **Ollama**, a **REST API**, or a **vendor SDK** outside the built-in component graph. +- You still want **consistent guard behavior** and optionally a **`PipelineResult`**-shaped trace for observability. + +## Supported targets + +- A **callable**: `(dict[str, Any]) -> Any` +- An object with **`run(self, dict[str, Any]) -> Any`** (preferred when both `run` and `__call__` exist) + +Return values are coerced to **`dict[str, Any]`** from: + +- `dict` / **`Mapping`** +- Objects with a **`model_dump()`** that returns a mapping (e.g. Pydantic-style) + +## Example + +```python +from oris.integrations import SafeRunner +from oris.rai.policy import PolicyEnforcer + +def my_external_llm(payload: dict) -> dict: + q = payload.get("query", "") + # ... call your model ... + return {"output": f"stub response for: {q!r}"} + +runner = SafeRunner(my_external_llm, policy=PolicyEnforcer()) + +plain = runner.run({"query": "Hello"}) +traced = runner.run({"query": "Hello"}, include_trace=True) +print(traced.to_run_summary()["trace"][0]["flags"]) +``` + +With **`include_trace=True`**, the trace includes a single external step with **`flags`** such as `"kind": "external_pipeline"`. + +## Errors + +- **`GuardViolationError`** — Input or output failed policy (same as in-framework runs). +- **`PipelineExecutionError`** — Invalid target, non-mapping output, or wrapped external failure (see source docstrings for semantics). + +## Notebook + +The repository’s [**LLM integration**](../examples/index.md) notebook demonstrates optional Ollama + mock fallback behind **`SafeRunner`**. + +## Related + +- [**RAI & observability**](../concepts/rai-tracing.md) +- [**Runs, output & traces**](output.md) diff --git a/docs/oris_logo.png b/docs/images/oris_logo.png similarity index 100% rename from docs/oris_logo.png rename to docs/images/oris_logo.png diff --git a/docs/index.md b/docs/index.md index 15f6cee..f664bb5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,85 +5,49 @@ hide: # Introduction to Oris -**Oris** is an open-source **Responsible AI** pipeline **runtime** for Python. You define pipelines as YAML or build them in code: each run moves through validation, guards, execution, and tracing so you can ship AI workflows with clearer safety and observability defaults. +**Oris** is an open-source **Responsible AI runtime** for Python. Define **pipelines** in YAML (or build them in code), run them through a single **executor**, and get **input and output policy checks** plus **run- and step-level traces** by default—so you can ship LLM and automation workflows with clearer safety defaults and better observability. -The core ideas are **components** wired into a **pipeline**, **providers** for backends, **RAI guards** on inputs and outputs, and **traces** at run and step granularity. Oris stays **framework-agnostic** at the boundary you integrate with: anything that can be invoked like a `run(...)` step can participate in the same executor model. +The design is **modular and framework-agnostic**: built-in **components** and optional **LLM providers** compose into a validated plan; anything you can invoke like `run(dict)` can also sit behind [**SafeRunner**](guides/safe-runner.md) with the same policy surface. -If you already know you want to try it, skip ahead to **[Install](#install)** and **[Quick start](#quick-start)**. For depth on design and guarantees, use the left-hand navigation — **Architecture**, **Public API**, **Security**, **Provider design**, **Releases**, and **Contributing** are synced from the repository root on every docs build. +![Oris logo](images/oris_logo.png){ width="140" } -![Oris logo](oris_logo.png){ width="140" } +!!! tip "Welcome to Oris" -!!! note "Built from this repository" - These pages are generated with **MkDocs Material**. Policy and design pages (**Architecture** through **Code of Conduct**, including **Provider design**) are copied from the repo root during `scripts/sync_doc_sources.sh` (including in CI), so the site matches `prod` sources. Only **`index.md`** and assets under **`docs/`** are edited in place. + To go straight from install to a working pipeline, see [**Installation**](get-started/installation.md) and [**Quickstart**](get-started/quickstart.md). ## What you can build with Oris -**Production-style pipeline runs** -Load YAML definitions or construct pipelines programmatically, then execute with a single `run` API and consistent error and trace shapes. +**Guarded pipeline runs** +Load YAML or construct plans in Python, call `Pipeline.run`, and rely on consistent validation, guard hooks, and trace shapes—suitable for services and batch jobs. -**Guarded execution** -Apply input and output policies so risky or sensitive content is checked before and after model-facing steps. +**Observable LLM-style workflows** +Use template and provider-backed steps, inspect `PipelineResult` and `to_run_summary()` for structured logs and governance-friendly exports. -**Observable workflows** -Inspect run and step traces to debug behavior and support governance without ad hoc logging only. +**External models and tools** +Wrap Ollama, HTTP APIs, or proprietary runtimes with **SafeRunner** so external code passes through the same **PolicyEnforcer** as in-framework execution. -**CLI and library use** -Validate definitions and run pipelines from the terminal, or embed Oris in services with the same definitions. +**CLI-first operations** +Validate definitions and run pipelines from the terminal with `oris validate` and `oris run`, matching what you do in code. -## Install +## How Oris is organized -```bash -pip install oris-ai -``` +Oris centers on **pipelines**, **components**, **providers**, **RAI policy** (input/output guards), and **tracing**. Configuration is parsed with safe YAML loading and validated before execution. Read more in the [**Concepts**](concepts/overview.md) section. -For working on Oris itself, clone the repo and use an editable install: +!!! note "Inspiration" -```bash -pip install -e ".[dev]" -``` - -## Quick start - -Save the following as **`pipeline.yaml`** (any path you prefer): - -```yaml -name: basic_pipeline -components: - - type: passthrough - name: normalize_input - - type: template_response - name: responder - config: - template: "AI answer placeholder for: {query}" -``` - -**Python** (from the same directory as the file): - -```python -from oris import Pipeline - -result = Pipeline.from_yaml("pipeline.yaml").run({"query": "What is AI?"}) -print(result.output) -``` - -**CLI:** - -```bash -oris validate pipeline.yaml -oris run pipeline.yaml --input-json '{"query":"What is AI?"}' -``` + This documentation site follows the same *intro → get started → concepts → guides* flow popularized by frameworks such as [**Haystack**](https://docs.haystack.deepset.ai/docs/intro)—clear navigation, skimmable pages, and copy-friendly code blocks. ## Next steps -| Goal | Where to go | +| Goal | Start here | | :--- | :--- | -| System shape and modules | [Architecture](ARCHITECTURE.md) | -| Stable surface for integrators | [Public API](PUBLIC_API.md) | -| Provider YAML, registry, and injection | [Provider design](PROVIDER_DESIGN.md) | -| Reporting vulnerabilities | [Security](SECURITY.md) | -| Releases and versioning | [Releases](RELEASE.md) | -| How to contribute | [Contributing](CONTRIBUTING.md) | +| Install the package | [**Installation**](get-started/installation.md) | +| Run a minimal pipeline | [**Quickstart**](get-started/quickstart.md) | +| Understand the mental model | [**Concepts overview**](concepts/overview.md) | +| Command-line usage | [**CLI reference**](guides/cli.md) | +| Notebooks & samples | [**Examples**](examples/index.md) | +| Report issues or contribute | [**Contributing**](development/contributing.md) | ## Repository -Source and issues: [github.com/DevStrikerTech/oris](https://github.com/DevStrikerTech/oris) +Source, issues, and releases: [github.com/DevStrikerTech/oris](https://github.com/DevStrikerTech/oris) · PyPI: [`oris-ai`](https://pypi.org/project/oris-ai/) diff --git a/examples/basic_pipeline.ipynb b/examples/basic_pipeline.ipynb new file mode 100644 index 0000000..cb97168 --- /dev/null +++ b/examples/basic_pipeline.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9f023644", + "metadata": {}, + "source": [ + "# Basic Oris pipeline\n", + "\n", + "Load the sample YAML under `examples/`, run it with `Pipeline.run`, and inspect the **output** and **trace summary**.\n", + "\n", + "**Tip:** Run Jupyter with the working directory set to the **repository root** or to **`examples/`** so the helper below can find `simple_generation.yaml`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "698cef7e", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:37.475462Z", + "iopub.status.busy": "2026-04-08T19:07:37.475158Z", + "iopub.status.idle": "2026-04-08T19:07:37.490919Z", + "shell.execute_reply": "2026-04-08T19:07:37.490010Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PosixPath('/Users/nishatahmed/PycharmProjects/oris/examples')" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pathlib import Path\n", + "\n", + "\n", + "def examples_dir() -> Path:\n", + " here = Path.cwd().resolve()\n", + " if (here / \"simple_generation.yaml\").exists():\n", + " return here\n", + " ex = here / \"examples\"\n", + " if (ex / \"simple_generation.yaml\").exists():\n", + " return ex\n", + " raise FileNotFoundError(\n", + " \"Could not find simple_generation.yaml — open Jupyter from repo root or examples/.\"\n", + " )\n", + "\n", + "\n", + "EX = examples_dir()\n", + "EX" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "85028b44", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:37.493470Z", + "iopub.status.busy": "2026-04-08T19:07:37.493240Z", + "iopub.status.idle": "2026-04-08T19:07:37.529085Z", + "shell.execute_reply": "2026-04-08T19:07:37.528554Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- result.output ---\n", + "{'output': 'Response: What is responsible AI?',\n", + " 'query': 'What is responsible AI?'}\n" + ] + } + ], + "source": [ + "from pprint import pprint\n", + "\n", + "from oris import Pipeline\n", + "\n", + "pipeline = Pipeline.from_yaml(EX / \"simple_generation.yaml\")\n", + "result = pipeline.run({\"query\": \"What is responsible AI?\"})\n", + "\n", + "print(\"--- result.output ---\")\n", + "pprint(result.output)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7b2107f0", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:37.530688Z", + "iopub.status.busy": "2026-04-08T19:07:37.530541Z", + "iopub.status.idle": "2026-04-08T19:07:37.533235Z", + "shell.execute_reply": "2026-04-08T19:07:37.532471Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- to_run_summary() (structured CLI-style view) ---\n", + "{\n", + " \"run_id\": \"99598aef-d05c-45f9-ba5c-c6a9e55294da\",\n", + " \"status\": \"success\",\n", + " \"output\": {\n", + " \"query\": \"What is responsible AI?\",\n", + " \"output\": \"Response: What is responsible AI?\"\n", + " },\n", + " \"trace\": [\n", + " {\n", + " \"step_id\": \"pipeline_pre_0\",\n", + " \"component_name\": \"pipeline_pre_0\",\n", + " \"status\": \"success\",\n", + " \"latency_ms\": 0.011,\n", + " \"flags\": {\n", + " \"kind\": \"pipeline_hook\",\n", + " \"phase\": \"pre\",\n", + " \"index\": 0\n", + " }\n", + " },\n", + " {\n", + " \"step_id\": \"answer\",\n", + " \"component_name\": \"answer\",\n", + " \"status\": \"success\",\n", + " \"latency_ms\": 0.005,\n", + " \"flags\": {\n", + " \"kind\": \"pipeline_step\",\n", + " \"step_index\": 0,\n", + " \"total_steps\": 1\n", + " }\n", + " },\n", + " {\n", + " \"step_id\": \"pipeline_post_0\",\n", + " \"component_name\": \"pipeline_post_0\",\n", + " \"status\": \"success\",\n", + " \"latency_ms\": 0.006,\n", + " \"flags\": {\n", + " \"kind\": \"pipeline_hook\",\n", + " \"phase\": \"post\",\n", + " \"index\": 0\n", + " }\n", + " }\n", + " ]\n", + "}\n" + ] + } + ], + "source": [ + "import json\n", + "\n", + "summary = result.to_run_summary()\n", + "print(\"--- to_run_summary() (structured CLI-style view) ---\")\n", + "print(json.dumps(summary, indent=2, default=str))" + ] + }, + { + "cell_type": "markdown", + "id": "6b0a410a", + "metadata": {}, + "source": [ + "## Trace / debug-style fields\n", + "\n", + "Each entry in `summary[\"trace\"]` includes `step_id`, `component_name`, `status`, `latency_ms`, and `flags`. Use `oris run ... --debug` on the CLI to print similar step lines to **stderr** while keeping JSON on stdout." + ] + } + ], + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/llm_integration.ipynb b/examples/llm_integration.ipynb new file mode 100644 index 0000000..21c8a9d --- /dev/null +++ b/examples/llm_integration.ipynb @@ -0,0 +1,257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "51f0f2d3", + "metadata": {}, + "source": [ + "# External LLM workflow (Ollama optional + mock)\n", + "\n", + "This notebook shows a **real-world pattern**: call an HTTP API (Ollama on `localhost` if present), otherwise fall back to a **mock** response—no GPU and no extra Python dependencies. The callable is then wrapped with **`SafeRunner`** so RAI checks apply to the same payload shape you would use in production." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "2fcadb82", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:42.062395Z", + "iopub.status.busy": "2026-04-08T19:07:42.062094Z", + "iopub.status.idle": "2026-04-08T19:07:42.104593Z", + "shell.execute_reply": "2026-04-08T19:07:42.103850Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'output': '[mock LLM] Say hello in one word.',\n", + " 'source': 'mock',\n", + " 'model': 'qwen2'}" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import json\n", + "import urllib.error\n", + "import urllib.request\n", + "from typing import Any\n", + "\n", + "\n", + "def chat_ollama_or_mock(prompt: str, model: str = \"qwen2\", timeout: float = 2.0) -> dict[str, Any]:\n", + " \"\"\"Try Ollama /api/generate; on any failure return a deterministic mock.\"\"\"\n", + " body = json.dumps({\"model\": model, \"prompt\": prompt, \"stream\": False}).encode()\n", + " req = urllib.request.Request(\n", + " \"http://127.0.0.1:11434/api/generate\",\n", + " data=body,\n", + " headers={\"Content-Type\": \"application/json\"},\n", + " method=\"POST\",\n", + " )\n", + " try:\n", + " with urllib.request.urlopen(req, timeout=timeout) as resp:\n", + " data = json.loads(resp.read().decode())\n", + " text = (data.get(\"response\") or \"\").strip()\n", + " return {\"output\": text, \"source\": \"ollama\", \"model\": model}\n", + " except (OSError, urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError, ValueError):\n", + " return {\n", + " \"output\": f\"[mock LLM] {prompt[:200]}\",\n", + " \"source\": \"mock\",\n", + " \"model\": model,\n", + " }\n", + "\n", + "\n", + "demo = chat_ollama_or_mock(\"Say hello in one word.\")\n", + "demo" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4a4c27c3", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:42.106445Z", + "iopub.status.busy": "2026-04-08T19:07:42.106287Z", + "iopub.status.idle": "2026-04-08T19:07:42.135610Z", + "shell.execute_reply": "2026-04-08T19:07:42.135200Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'output': '[mock LLM] Explain RAI in one sentence.', 'source': 'mock', 'model': 'qwen2'}\n", + "--- trace flags --- {'kind': 'external_pipeline'}\n" + ] + } + ], + "source": [ + "from oris.integrations import SafeRunner\n", + "from oris.rai.policy import PolicyEnforcer\n", + "\n", + "policy = PolicyEnforcer()\n", + "\n", + "\n", + "def llm_step(payload: dict) -> dict:\n", + " prompt = str(payload.get(\"query\", \"\"))\n", + " return chat_ollama_or_mock(prompt)\n", + "\n", + "\n", + "safe_llm = SafeRunner(llm_step, policy=policy)\n", + "result = safe_llm.run({\"query\": \"Explain RAI in one sentence.\"}, include_trace=True)\n", + "print(result.output)\n", + "print(\"--- trace flags ---\", result.to_run_summary()[\"trace\"][0][\"flags\"])" + ] + }, + { + "cell_type": "markdown", + "id": "78c8d614", + "metadata": {}, + "source": [ + "## Same idea with a YAML pipeline + provider stub\n", + "\n", + "Built-in **`openai`** / **`huggingface`** providers are **stubs** (no network): they only require the named env var to be set. Below we set a **non-secret placeholder** so the file loads; output is deterministic and safe for demos." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "55631b3b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:42.137246Z", + "iopub.status.busy": "2026-04-08T19:07:42.137127Z", + "iopub.status.idle": "2026-04-08T19:07:42.142211Z", + "shell.execute_reply": "2026-04-08T19:07:42.141821Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'query': 'What is a pipeline?',\n", + " 'output': '[openai:gpt-4] What is a pipeline?'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "from pathlib import Path\n", + "\n", + "from oris import Pipeline\n", + "\n", + "\n", + "def examples_dir() -> Path:\n", + " here = Path.cwd().resolve()\n", + " if (here / \"provider_pipeline.yaml\").exists():\n", + " return here\n", + " ex = here / \"examples\"\n", + " if (ex / \"provider_pipeline.yaml\").exists():\n", + " return ex\n", + " raise FileNotFoundError(\"Run from repo root or examples/.\")\n", + "\n", + "\n", + "os.environ.setdefault(\"OPENAI_API_KEY\", \"notebook-demo-not-a-real-key\")\n", + "EX2 = examples_dir()\n", + "pipe = Pipeline.from_yaml(EX2 / \"provider_pipeline.yaml\")\n", + "pr = pipe.run({\"query\": \"What is a pipeline?\"})\n", + "pr.output" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e86a5532", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:42.143511Z", + "iopub.status.busy": "2026-04-08T19:07:42.143414Z", + "iopub.status.idle": "2026-04-08T19:07:42.145542Z", + "shell.execute_reply": "2026-04-08T19:07:42.145078Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"run_id\": \"acddf7fa-56c7-431e-ab3f-196503abb6b5\",\n", + " \"status\": \"success\",\n", + " \"output\": {\n", + " \"query\": \"What is a pipeline?\",\n", + " \"output\": \"[openai:gpt-4] What is a pipeline?\"\n", + " },\n", + " \"trace\": [\n", + " {\n", + " \"step_id\": \"pipeline_pre_0\",\n", + " \"component_name\": \"pipeline_pre_0\",\n", + " \"status\": \"success\",\n", + " \"latency_ms\": 0.008,\n", + " \"flags\": {\n", + " \"kind\": \"pipeline_hook\",\n", + " \"phase\": \"pre\",\n", + " \"index\": 0\n", + " }\n", + " },\n", + " {\n", + " \"step_id\": \"step1\",\n", + " \"component_name\": \"step1\",\n", + " \"status\": \"success\",\n", + " \"latency_ms\": 0.006,\n", + " \"flags\": {\n", + " \"kind\": \"pipeline_step\",\n", + " \"step_index\": 0,\n", + " \"total_steps\": 1\n", + " }\n", + " },\n", + " {\n", + " \"step_id\": \"pipeline_post_0\",\n", + " \"component_name\": \"pipeline_post_0\",\n", + " \"status\": \"success\",\n", + " \"latency_ms\": 0.004,\n", + " \"flags\": {\n", + " \"kind\": \"pipeline_hook\",\n", + " \"phase\": \"post\",\n", + " \"index\": 0\n", + " }\n", + " }\n", + " ]\n", + "}\n" + ] + } + ], + "source": [ + "import json\n", + "\n", + "print(json.dumps(pr.to_run_summary(), indent=2, default=str))" + ] + } + ], + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/safe_runner.ipynb b/examples/safe_runner.ipynb new file mode 100644 index 0000000..aed12cc --- /dev/null +++ b/examples/safe_runner.ipynb @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2b358135", + "metadata": {}, + "source": [ + "# SafeRunner integration\n", + "\n", + "Wrap a plain function (or an object with `run(dict)`) with the same **RAI policy** as the main executor. Use `include_trace=True` for a `PipelineResult` compatible with `to_run_summary()`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e9bebbbd", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:39.773651Z", + "iopub.status.busy": "2026-04-08T19:07:39.773438Z", + "iopub.status.idle": "2026-04-08T19:07:39.809942Z", + "shell.execute_reply": "2026-04-08T19:07:39.809423Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'answer': \"Processed: 'Hello from SafeRunner'\", 'ok': True}" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from oris.core.exceptions import GuardViolationError\n", + "from oris.integrations import SafeRunner\n", + "from oris.rai.policy import PolicyEnforcer\n", + "\n", + "policy = PolicyEnforcer()\n", + "\n", + "\n", + "def my_app(data: dict) -> dict:\n", + " q = data.get(\"query\", \"\")\n", + " return {\"answer\": f\"Processed: {q!r}\", \"ok\": True}\n", + "\n", + "\n", + "runner = SafeRunner(my_app, policy=policy)\n", + "out = runner.run({\"query\": \"Hello from SafeRunner\"})\n", + "out" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d6c484d6", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:39.811264Z", + "iopub.status.busy": "2026-04-08T19:07:39.811144Z", + "iopub.status.idle": "2026-04-08T19:07:39.813763Z", + "shell.execute_reply": "2026-04-08T19:07:39.813324Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('success', 'external_pipeline', {'kind': 'external_pipeline'})" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "traced = runner.run({\"query\": \"Trace me\"}, include_trace=True)\n", + "summ = traced.to_run_summary()\n", + "summ[\"status\"], summ[\"trace\"][0][\"step_id\"], summ[\"trace\"][0][\"flags\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5eb0d244", + "metadata": { + "execution": { + "iopub.execute_input": "2026-04-08T19:07:39.814942Z", + "iopub.status.busy": "2026-04-08T19:07:39.814841Z", + "iopub.status.idle": "2026-04-08T19:07:39.817054Z", + "shell.execute_reply": "2026-04-08T19:07:39.816371Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Blocked by input policy: Input contains prohibited key 'secret'.\n" + ] + } + ], + "source": [ + "try:\n", + " runner.run({\"secret\": \"not-allowed-key\"})\n", + "except GuardViolationError as exc:\n", + " print(\"Blocked by input policy:\", exc)" + ] + }, + { + "cell_type": "markdown", + "id": "91c05cbc", + "metadata": {}, + "source": [ + "Consistent **dict** output on success; **`GuardViolationError`** when input or output policy rejects the payload." + ] + } + ], + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index ed43399..b24c6ff 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,48 +1,103 @@ -# Built by: scripts/sync_doc_sources.sh (or CI) then mkdocs build -site_name: Oris docs -site_description: Responsible AI pipeline runtime framework for Python (oris-ai). +# Oris documentation — built with MkDocs Material (GitHub Pages) +site_name: Oris +site_description: >- + Responsible AI pipeline runtime for Python — YAML pipelines, guards, traces, and SafeRunner. +site_author: Oris Contributors site_url: https://devstrikertech.github.io/oris/ repo_url: https://github.com/DevStrikerTech/oris repo_name: DevStrikerTech/oris edit_uri: edit/dev/docs/ +docs_dir: docs +site_dir: site + theme: name: material - logo: oris_logo.png - favicon: oris_logo.png + language: en + logo: images/oris_logo.png + favicon: images/oris_logo.png palette: - - scheme: default + - media: "(prefers-color-scheme: light)" + scheme: default primary: indigo + accent: indigo toggle: icon: material/brightness-7 name: Switch to dark mode - - scheme: slate + - media: "(prefers-color-scheme: dark)" + scheme: slate primary: indigo + accent: indigo toggle: icon: material/brightness-4 name: Switch to light mode + font: + text: Inter + code: JetBrains Mono features: + - content.code.copy + - content.action.edit - navigation.expand + - navigation.footer + - navigation.indexes + - navigation.instant + - navigation.instant.progress + - navigation.sections + - navigation.tabs - navigation.top + - navigation.tracking + - search.highlight - search.suggest - - content.code.copy + - toc.follow + +extra: + version: 0.7.2 + social: + - icon: fontawesome/brands/github + link: https://github.com/DevStrikerTech/oris + - icon: fontawesome/brands/python + link: https://pypi.org/project/oris-ai/ markdown_extensions: + - abbr + - admonition - attr_list + - pymdownx.details - pymdownx.highlight: anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets - pymdownx.superfences - - admonition + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true - toc: permalink: true nav: - - Home: index.md - - Architecture: ARCHITECTURE.md - - Public API: PUBLIC_API.md - - Engineering standards: ENGINEERING_STANDARDS.md - - Security: SECURITY.md - - Provider design: PROVIDER_DESIGN.md - - Releases: RELEASE.md - - Contributing: CONTRIBUTING.md - - Code of Conduct: CODE_OF_CONDUCT.md + - Introduction: index.md + - Get started: + - Installation: get-started/installation.md + - Quickstart: get-started/quickstart.md + - Concepts: + - Overview: concepts/overview.md + - Pipelines & YAML: concepts/pipelines.md + - Components & providers: concepts/components.md + - RAI & observability: concepts/rai-tracing.md + - Guides: + - CLI reference: guides/cli.md + - SafeRunner: guides/safe-runner.md + - Runs, output & traces: guides/output.md + - Examples: examples/index.md + - Community: + - Contributing: development/contributing.md + - Security: development/security.md + - Code of Conduct: development/code-of-conduct.md + +plugins: + - search + +copyright: Copyright © Oris Contributors — MIT License diff --git a/pyproject.toml b/pyproject.toml index c92f91f..bfb982c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "oris-ai" -version = "0.7.1" +version = "0.7.2" description = "Oris: Responsible AI runtime framework for production pipelines." readme = "README.md" requires-python = ">=3.10" @@ -30,14 +30,18 @@ dependencies = [ [project.optional-dependencies] dev = [ + "ipykernel>=6.29.0", + "nbconvert>=7.17.0", "mypy>=1.11.0", - "mkdocs-material>=9.5.0", "pre-commit>=3.8.0", "pytest>=8.3.2", "pytest-cov>=5.0.0", "ruff>=0.6.4", "types-PyYAML>=6.0.12.20240917", ] +docs = [ + "mkdocs-material>=9.5.0", +] [project.scripts] oris = "oris.cli.main:main" diff --git a/scripts/sync_doc_sources.sh b/scripts/sync_doc_sources.sh deleted file mode 100755 index 9b0c782..0000000 --- a/scripts/sync_doc_sources.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -cd "$ROOT" -for f in ARCHITECTURE.md ENGINEERING_STANDARDS.md SECURITY.md PUBLIC_API.md RELEASE.md CONTRIBUTING.md CODE_OF_CONDUCT.md PROVIDER_DESIGN.md; do - if [[ -f "$f" ]]; then - cp "$f" docs/ - fi -done