Skip to content

Commit 20a1557

Browse files
Merge pull request #8 from pnnl/ai-approaches
Ai approaches
2 parents fa2aaf7 + 7ec2576 commit 20a1557

17 files changed

Lines changed: 974 additions & 3 deletions

README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,65 @@ pip install comcheckweb-api-python
4141

4242
For detailed usage examples and API reference, see the [documentation](https://pnnl-int.github.io/comcheckweb-api-python/).
4343

44+
## Introspection helpers
45+
46+
The package ships typed helpers for discovering what the SDK exposes
47+
and for validating project data — useful from notebooks, IDE plugins,
48+
and AI agents alike. All return Pydantic models; call `.model_dump()`
49+
when you need JSON.
50+
51+
```python
52+
import comcheck_api as cc
53+
54+
# What operation functions does the SDK ship?
55+
for op in cc.list_operations():
56+
print(op.group, op.signature)
57+
58+
# What does the ComBuilding model look like?
59+
schema = cc.lookup_type("ComBuilding")
60+
for field in schema.fields:
61+
print(field.name, field.type, field.required)
62+
63+
# Does this dict satisfy the SDK schema?
64+
result = cc.validate_project(project_dict)
65+
if not result.ok:
66+
for err in result.errors:
67+
print(err.loc, err.msg)
68+
```
69+
70+
See [`api/introspection`](https://pnnl-int.github.io/comcheckweb-api-python/api/introspection/)
71+
in the docs for the full reference.
72+
73+
## AI integration: the Claude Skill
74+
75+
A bundled Claude Skill teaches Claude how to use this SDK correctly —
76+
operation modules, default templates, the simulation polling loop,
77+
common pitfalls. The Skill folder lives at
78+
[`comcheck_api/ai/skill/`](comcheck_api/ai/skill/) and ships in the
79+
wheel.
80+
81+
### Setup in your own repo
82+
83+
Install the bundled Skill into a project-level
84+
`.claude/skills/comcheck-api/`. Claude Code scans
85+
`<project>/.claude/skills/` when a session opens against the repo,
86+
so the guidance kicks in only for projects that actually use this
87+
SDK — not on every Claude session everywhere.
88+
89+
```bash
90+
# Run this once in the root of the project that consumes comcheck_api:
91+
comcheck-api install-skill
92+
```
93+
94+
Commit `.claude/skills/comcheck-api/`. Teammates get the same
95+
guidance the moment they open the repo in Claude Code, and Claude
96+
can pull in the reference docs, examples, and `validate_code.py`
97+
script on demand — not just the SKILL.md body. Re-run the command
98+
with `--force` after upgrading the package to refresh the skill.
99+
100+
To install globally for every Claude session instead of per-project,
101+
pass `--global` (writes to `~/.claude/skills/comcheck-api/`).
102+
44103
## Development
45104

46105
Clone the repository and follow the commands below to set up developer tooling.
@@ -74,6 +133,26 @@ uv sync
74133
- Run type checking:
75134
`uv run mypy comcheck_api`
76135

136+
## Running the docs locally
137+
138+
The documentation site is built with MkDocs (Material theme +
139+
mkdocstrings). The dependencies live in the optional `docs` group
140+
defined in `pyproject.toml`.
141+
142+
```bash
143+
# Install the docs group (mkdocs, mkdocs-material, mkdocstrings).
144+
uv sync --group docs
145+
146+
# Serve with live reload at http://127.0.0.1:8000
147+
uv run mkdocs serve
148+
149+
# One-shot build into ./site/
150+
uv run mkdocs build
151+
152+
# Fail on warnings (good before committing)
153+
uv run mkdocs build --strict
154+
```
155+
77156
## Support
78157

79158
This is a publicly available library maintained by PNNL. While the code is open source and free to use, **external contributions are not accepted** at this time.

comcheck_api/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
project_envelope_operations,
6161
)
6262

63+
# Introspection helpers
64+
from .introspection import list_operations, lookup_type
65+
66+
# Validation helpers
67+
from .validation import validate_project
68+
6369
# Utilities
6470
from . import utilities
6571

@@ -81,6 +87,11 @@
8187
# Project Operations
8288
"project_building_area_operations",
8389
"project_envelope_operations",
90+
# Introspection
91+
"list_operations",
92+
"lookup_type",
93+
# Validation
94+
"validate_project",
8495
# Utilities
8596
"utilities",
8697
# Types

comcheck_api/ai/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""AI-facing surface for the comcheck_api package.
2+
3+
Ships the canonical Skill folder under ``comcheck_api/ai/skill/``
4+
(SKILL.md + reference docs + examples + scripts). Use
5+
:func:`skill_root` to get the on-disk path to the bundled folder —
6+
useful for installing it into ``<project>/.claude/skills/`` or
7+
``~/.claude/skills/``.
8+
9+
Introspection and validation helpers live on the SDK itself
10+
(``comcheck_api.list_operations``, ``comcheck_api.lookup_type``,
11+
``comcheck_api.validate_project``).
12+
"""
13+
14+
from __future__ import annotations
15+
16+
from importlib.resources import as_file, files
17+
from pathlib import Path
18+
19+
20+
def skill_root() -> Path:
21+
"""Return the on-disk path to the bundled Skill folder.
22+
23+
Uses ``importlib.resources`` so the path resolves correctly
24+
whether the package is installed normally or zip-imported.
25+
"""
26+
with as_file(files("comcheck_api.ai.skill")) as p:
27+
return Path(p)
28+
29+
30+
__all__ = ["skill_root"]

comcheck_api/ai/skill/SKILL.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
name: comcheck-api
3+
description: Use this skill when the user is writing Python code that uses
4+
the comcheck_api package to build COMcheck project JSON, run compliance
5+
simulations against the PNNL COMcheck Web API, or work with envelope,
6+
lighting, mechanical, or building-area operations. Triggers on imports
7+
of `comcheck_api`, mentions of COMcheck/ASHRAE 90.1/IECC compliance,
8+
or requests to validate building energy code compliance.
9+
---
10+
11+
# COMcheck API Python Client Skill
12+
13+
## When to use this skill
14+
15+
Triggers:
16+
- The user imports `comcheck_api` or asks for help with it.
17+
- The user mentions COMcheck, ASHRAE 90.1, IECC, or commercial building
18+
energy code compliance.
19+
- The user wants to build, update, or run a simulation on a COMcheck
20+
project from Python.
21+
22+
## Core concepts
23+
24+
- **Single entry point**: `COMcheckClient` is the only client class
25+
users instantiate. Construct with `api_key=...` or rely on the
26+
`COM_API_KEY` env var.
27+
- **Project shape**: a project is a `ComBuilding` Pydantic model. It
28+
contains: `Project` (metadata), `Location`, `Envelope`, `WholeBldgUse[]`
29+
(building areas), `HVAC`, `Lighting`, `Renewable`, and `Control`.
30+
- **Operation classes (functional)**: building areas and envelope
31+
components are added/updated/removed via free functions in
32+
`project_building_area_operations` and `project_envelope_operations`.
33+
Each function takes a `ComBuilding` and returns a new `ComBuilding`.
34+
- **Defaults**: `comcheck_api.defaults` has `get_default_*_template()`
35+
functions that return Pydantic models filled with sensible defaults.
36+
Always start from these.
37+
- **Simulation flow is async**: `start_run_simulation` returns a
38+
session ID. Poll `get_simulation_status` until status is `complete`,
39+
then call `get_simulation_result`.
40+
41+
## Quick start
42+
43+
```python
44+
from comcheck_api import COMcheckClient
45+
from comcheck_api.defaults import get_default_project_template
46+
47+
client = COMcheckClient(api_key="your-key")
48+
49+
# Build a project from a default template
50+
project = get_default_project_template()
51+
project.Project.title = "5,000 sqft office in Seattle"
52+
53+
# Save it (creates server-side project, returns ID via list)
54+
# Update if you have an existing ID:
55+
# updated = client.update_project(project_id="123", project_data=project)
56+
57+
# Run a simulation
58+
session_id = client.start_run_simulation(project)
59+
60+
# Poll until complete
61+
import time
62+
while True:
63+
status = client.get_simulation_status(session_id)
64+
if status["status"] == "complete":
65+
break
66+
time.sleep(5)
67+
68+
result = client.get_simulation_result(session_id)
69+
print(result["performanceRating"])
70+
```
71+
72+
## Conventions (do)
73+
74+
- Use `get_default_*_template()` to start any new component, then
75+
customize. Don't construct `ComBuilding` from scratch.
76+
- Use the operation classes (e.g.,
77+
`project_envelope_operations.add_ag_wall_to_project(project, wall)`)
78+
to mutate project structure. Don't manipulate nested dicts directly.
79+
- Read the API key from `COM_API_KEY` env var by default; let users
80+
pass `api_key=...` to override.
81+
- Wrap network calls in try/except and catch `COMCheckHTTPError`,
82+
`COMCheckConnectionError`, `COMCheckValidationError`,
83+
`COMCheckSimulationError`, `COMCheckProjectNotFoundError`.
84+
- The package uses `httpx`, not `requests`.
85+
86+
## Conventions (don't)
87+
88+
- Don't construct project JSON by hand — use templates + operation
89+
functions.
90+
- Don't suggest `requests` — the SDK is `httpx`-based.
91+
- Don't import private modules (anything starting with `_`).
92+
- Don't poll `get_simulation_status` faster than every 5 seconds.
93+
- Don't put the API key in source code; use env var or argument.
94+
95+
## Common patterns
96+
97+
### Adding an above-grade wall
98+
99+
```python
100+
from comcheck_api import project_envelope_operations as envelope_ops
101+
from comcheck_api.defaults import get_default_ag_wall_template
102+
103+
wall = get_default_ag_wall_template()
104+
wall.name = "South wall"
105+
wall.area = 4800.0
106+
project = envelope_ops.add_ag_wall_to_project(project, wall)
107+
```
108+
109+
### Listing the user's projects and updating one
110+
111+
```python
112+
projects = client.list_projects()
113+
target = next(p for p in projects if p["title"] == "My office")
114+
project_obj = client.get_project(target["id"])
115+
project_obj.Project.title = "My office (revised)"
116+
client.update_project(project_id=target["id"], project_data=project_obj)
117+
```
118+
119+
### Polling a simulation with a timeout
120+
121+
```python
122+
import time
123+
session_id = client.start_run_simulation(project)
124+
deadline = time.time() + 300 # 5 min
125+
while time.time() < deadline:
126+
status = client.get_simulation_status(session_id)
127+
if status["status"] == "complete":
128+
result = client.get_simulation_result(session_id)
129+
break
130+
time.sleep(5)
131+
else:
132+
raise TimeoutError(f"Simulation {session_id} did not complete in 5 min")
133+
```
134+
135+
## When you need more detail
136+
137+
- For envelope assemblies (roof, walls, floor, windows, doors,
138+
skylights, thermal bridges) → read `reference/operations.md`.
139+
- For Pydantic model field-level details → read `reference/types.md`.
140+
- For the simulation start/poll/fetch flow → read
141+
`reference/simulation.md`.
142+
- To validate generated code against a mocked client → run
143+
`scripts/validate_code.py`.

comcheck_api/ai/skill/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Canonical Skill folder for the comcheck_api package.
2+
3+
Hand-authored content. Use ``comcheck_api.ai.skill_root()`` to get
4+
the on-disk path.
5+
"""
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Project Operations Reference
2+
3+
Operation functions are free functions in two modules:
4+
5+
- `comcheck_api.project_operations.project_building_area_operations`
6+
- `comcheck_api.project_operations.project_envelope_operations`
7+
8+
Each function takes a `ComBuilding` and a payload, and returns a new
9+
`ComBuilding`. Treat them as immutable transformations.
10+
11+
## Building area operations
12+
13+
```python
14+
from comcheck_api import project_building_area_operations as ba_ops
15+
```
16+
17+
| Function | Purpose |
18+
|---|---|
19+
| `add_building_area_to_project(project, new_building_area)` | Add a `WholeBldgUse` building area to the project. |
20+
| `update_building_area_in_project(project, building_area_key, updates)` | Update fields of an existing building area by key. |
21+
| `remove_building_area_from_project(project, building_area_key)` | Remove a building area by key. |
22+
| `get_building_area_keys_from_project(project)` | List `[{key, areaDescription}, …]` for the project. |
23+
24+
## Envelope operations
25+
26+
```python
27+
from comcheck_api import project_envelope_operations as env_ops
28+
```
29+
30+
Each envelope component has a consistent triplet:
31+
`add_*_to_project`, `update_*_in_project`, `remove_*_from_project`.
32+
Plus the special `add_thermal_bridge_to_project` (no
33+
update/remove yet).
34+
35+
| Component | Add | Update | Remove |
36+
|---|---|---|---|
37+
| Roof | `add_roof_to_project` | `update_roof_in_project` | `remove_roof_from_project` |
38+
| Above-grade wall | `add_ag_wall_to_project` | `update_ag_wall_in_project` | `remove_ag_wall_from_project` |
39+
| Below-grade wall | `add_bg_wall_to_project` | `update_bg_wall_in_project` | `remove_bg_wall_from_project` |
40+
| Floor | `add_floor_to_project` | `update_floor_in_project` | `remove_floor_from_project` |
41+
| Skylight | `add_skylight_to_project` | `update_skylight_in_project` | `remove_skylight_from_project` |
42+
| Window | `add_window_to_project` | `update_window_in_project` | `remove_window_from_project` |
43+
| Door | `add_door_to_project` | `update_door_in_project` | `remove_door_from_project` |
44+
| Thermal bridge | `add_thermal_bridge_to_project` |||
45+
46+
## Nesting rules
47+
48+
- Skylights nest under a `Roof`.
49+
- Windows, doors, thermal bridges nest under an above-grade or
50+
below-grade wall.
51+
- Floors are top-level.
52+
53+
The `add_*_to_project` functions accept the parent component
54+
identifier (or auto-place under the first matching parent) — see
55+
the existing `examples/project_operations/envelope_operations.py`
56+
for current calling conventions.
57+
58+
## Working with templates
59+
60+
Always start from `comcheck_api.defaults`:
61+
62+
```python
63+
from comcheck_api.defaults import (
64+
get_default_project_template,
65+
get_default_roof_template,
66+
get_default_ag_wall_template,
67+
get_default_bg_wall_template,
68+
get_default_floor_template,
69+
get_default_window_template,
70+
get_default_door_template,
71+
get_default_skylight_template,
72+
get_default_thermal_bridge_template,
73+
get_default_building_area_template,
74+
)
75+
```
76+
77+
Each returns a fully-populated Pydantic model with sensible defaults
78+
(Boulder, CO; metal-frame walls; double-pane low-E glazing; etc.).
79+
Customize fields after construction:
80+
81+
```python
82+
roof = get_default_roof_template()
83+
roof.area = 6000.0
84+
roof.cavityRValue = 38.0
85+
project = env_ops.add_roof_to_project(project, roof)
86+
```

0 commit comments

Comments
 (0)