Skip to content

feat(hosting): shared engineered-features module + model package export - #40

Open
helder-moreira wants to merge 17 commits into
mainfrom
feat/engn-hosting-phase0-shared-features
Open

feat(hosting): shared engineered-features module + model package export#40
helder-moreira wants to merge 17 commits into
mainfrom
feat/engn-hosting-phase0-shared-features

Conversation

@helder-moreira

@helder-moreira helder-moreira commented Jul 17, 2026

Copy link
Copy Markdown

What

The builder-kit side of the hosting integration — lets a user who builds a model with the builder kit deploy it:

  1. Shared engineered-features module (apply_engineered_features) — one implementation used by BOTH the training path (walkthroughs/notebooks) and the generated serving code, so features are byte-identical on both → no train/serve skew.

  2. export (export_package / allora-forge-export CLI) — translates a builder-kit model into a deployable hosting package (the single-package format forge deploys):

    • forge_model/model.py — a generated allora_worker.BaseModel that reproduces the builder-kit model via AlloraMLWorkflow + the shared apply_engineered_features. Generic: reads PAIR/TIMEFRAME from env (set per deployment), so one package deploys to many pairs/timeframes.
    • forge_model/config.json — model-intrinsic config (features, target, hyperparameters, data source) baked in.
    • pyproject.toml — SDK unpinned (base image provides it), builder-kit installed from a git ref, entry-point keyed on model_type.
    • manifest.json — model-only (model_type, supports_training, has_weights, code_hash); no pair/timeframe/topic.
    • .dockerignore + optional bundled weights/.

Supports train-on-platform (code-only) and train-locally (bundled weights/).

Validated against the real builder-kit API: the generated model's train/predict pipeline matches the walkthroughs (feature_close_{i} base columns → apply_engineered_featuresfeature_colsdropna(subset=…+['target'])LGBMRegressor().fit), get_live_features provides attrs["current_price"], and api_key is passed only for the allora/atlas source (binance rejects it).

Usage

allora-forge-export --config model.json --out ./pkg [--weights ./w] [--builder-kit-ref <ref>]
# zip the contents of ./pkg and upload via forge (POST /api/v1/models)

Part of the hosting-integration set: allora-sdk-py#85, worker-allora-sdk-v2#13, forge-v2#260, allora-offchain-operator#53.

@helder-moreira
helder-moreira force-pushed the feat/engn-hosting-phase0-shared-features branch from 7b22bcd to afbd1f7 Compare July 17, 2026 15:49

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Agent Review — APPROVE

Panel: GPT-5.6 Sol · GLM 5.2 · Kimi K2.7 Code — all 3 reviewers returned APPROVE.

The shared engineered_features module is a clean, correct single source of truth for log-return features. The new vectorized computation is mathematically identical to the old inline engineer_returns: closes[-1] vs closes[-(w+1)] maps exactly to feature_close_{N-1} vs feature_close_{N-1-w}, and the guard conditions match. Both walkthrough .py files and the notebook now import the module in train and predict paths with the same ENGINEERED_SPECS, and all 6 parity tests pass.

Column naming change from log_return_1hlog_return_1 is intentional and correct: window_bars is interval-agnostic, so the same module works for 1h bars (topic 69) and 5m bars (topic 77) without hardcoded time-unit suffixes.

Findings

All findings are nits/suggestions — nothing blocking.

(Reviewed by Helder's Agent — GPT-5.6 Sol + GLM 5.2 + Kimi K2.7 Code)

Comment thread tests/test_live_features_consistency.py Outdated
Comment thread tests/test_live_features_consistency.py
Comment thread allora_forge_builder_kit/engineered_features.py
…ing Phase 0)

Extract the multi-horizon log-return features into apply_engineered_features so
training and serving compute them identically (no train/serve skew); refactor the
walkthroughs + notebook to use it; revive the parity test scoped to engineered features.
@helder-moreira

Copy link
Copy Markdown
Author

Addressed review: switched the unsupported-kind test to pytest.raises(ValueError, match='unsupported'), added a matching test for engineered_feature_names(), and documented the empty-frame scalar-broadcast behavior at the 0.0 fill. pytest → 7 passed.

@helder-moreira
helder-moreira force-pushed the feat/engn-hosting-phase0-shared-features branch from afbd1f7 to f072aa7 Compare July 17, 2026 16:22

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-Review — APPROVE

Panel: GPT-5.6 Sol · GLM 5.2 · DeepSeek V4 Pro — all 3 returned APPROVE.

Both prior nits are resolved:

  1. pytest.raises(ValueError, match="unsupported") now used in rejection tests ✅
  2. Dedicated test_engineered_feature_names_rejects_unsupported added, covering the engineered_feature_names code path ✅

All 7 parity tests pass. No new issues introduced by the force-push.

(Reviewed by Helder's Agent — GPT-5.6 Sol + GLM 5.2 + DeepSeek V4 Pro)

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final Review — APPROVE

Panel: GPT-5.6 Sol · DeepSeek V4 Pro · GLM 5.2 — unanimous APPROVE.

Third sign-off pass confirms merge-readiness. The shared engineered_features module correctly eliminates train/serve skew by routing both paths through apply_engineered_features with identical specs. All 7 unit tests pass (name stability, train==serve parity, reference computation, zero-fill guards, non-mutation, error handling). Both prior nits from round 1 remain fixed. No new commits since prior APPROVE — HEAD f072aa7 unchanged.

Verdict: Merge-ready.

(Reviewed by Helder's Agent — GPT-5.6 Sol + DeepSeek V4 Pro + GLM 5.2)

@helder-moreira helder-moreira changed the title feat(features): shared engineered-features module + parity test (hosting Phase 0) feat(hosting): shared engineered-features module + model package export Jul 20, 2026
@helder-moreira
helder-moreira force-pushed the feat/engn-hosting-phase0-shared-features branch from 03de0d4 to 014b152 Compare July 20, 2026 15:32
@helder-moreira helder-moreira changed the title feat(hosting): shared engineered-features module + model package export feat(export): package a bring-your-own model for hosting deploy Jul 20, 2026
@helder-moreira
helder-moreira force-pushed the feat/engn-hosting-phase0-shared-features branch 2 times, most recently from 03de0d4 to 898395a Compare July 20, 2026 15:45
@helder-moreira helder-moreira changed the title feat(export): package a bring-your-own model for hosting deploy feat(hosting): shared engineered-features module + model package export Jul 20, 2026
@helder-moreira
helder-moreira force-pushed the feat/engn-hosting-phase0-shared-features branch from 898395a to 3cf0b8c Compare July 20, 2026 15:46
Add `export_package` / `allora-forge-export` CLI that turns a builder-kit model
into an uploadable hosting package: generic env-driven model.py (PAIR/TIMEFRAME
from env, so one package deploys to many pairs/timeframes), config.json, pyproject
(SDK unpinned, builder-kit git ref, entry-point), manifest.json, .dockerignore,
and optional bundled weights/. Both train-on-platform and train-locally supported.
@helder-moreira
helder-moreira force-pushed the feat/engn-hosting-phase0-shared-features branch from 3cf0b8c to 11e1871 Compare July 20, 2026 15:57

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — APPROVE (with warning)

Panel: GPT-5.6 Sol (REQUEST_CHANGES — stale-file re-export) · GLM 5.2 (APPROVE) · Kimi K3 (APPROVE) · Claude Opus (pending)

The shared engineered-features module and parity tests remain sound (3 prior APPROVE rounds). The new package export feature is well-structured: ModelSpec validation, export_package() generating model.py/config.json/pyproject.toml/manifest.json, correct train/serve parity via apply_engineered_features, 21 tests passing.

Non-blocking warning (downgraded from GPT-5.6's critical)

Stale files on re-export to the same directory (export.py:139): If export_package is called with weights_dir and then re-called without it on the same out_dir, the stale weights/ directory remains while manifest.json reports has_weights: false. This is a consistency issue, not a correctness or security bug — the export function is a CLI tool typically run once per package, not iteratively. Consider exporting into a clean/staged directory or clearing out_dir before writing.

Parent-verification note

GPT-5.6 Sol cited line 239, but the actual stale-file concern is in the export_package function at lines 121+139. Line 239 is inside the _MODEL_PY template string. The finding is valid; the line number was inaccurate.

(Reviewed by Helder's Agent — GPT-5.6 Sol + GLM 5.2 + Kimi K3 + Claude Opus)

Comment thread allora_forge_builder_kit/export.py
@helder-moreira

Copy link
Copy Markdown
Author

Claude Opus 4.8 Review — APPROVE

The new single-package export feature is clean, well-documented, and thoroughly tested (21 tests pass locally). It correctly bakes model-intrinsic config into config.json while keeping pair/timeframe env-driven for deploy-time flexibility. Train/serve parity via apply_engineered_features is preserved.

[suggestion] export.py:310 — Generated code: LGBMRegressor(random_state=42, verbose=-1, **HYPERPARAMETERS) will raise TypeError if HYPERPARAMETERS contains random_state or verbose keys (duplicate keyword argument). Consider filtering known-conflicting keys from HYPERPARAMETERS before splatting, or document that users should not set these in hyperparameters.

[nit] export.py:268 — Minor inconsistency: the generated _workflow() gates api_key on SOURCE in ("allora", "atlas"), but ModelSpec.validate() only permits data_source in ("binance", "allora"). The "atlas" branch is dead code. Align the two lists or drop atlas.

(Claude Opus 4.8 via CLI — 4th reviewer)

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected Aggregated Review — COMMENT

Panel: GPT-5.6 Sol (REQUEST_CHANGES) · GLM 5.2 (APPROVE) · Kimi K3 (APPROVE) · Claude Opus 4.8 (APPROVE)

Corrected verdict: COMMENT. The prior review said APPROVE, but GPT-5.6 Sol returned REQUEST_CHANGES with a warning-level finding (downgraded from critical via severity rubric). Per merge-readiness criteria: zero criticals, warning not acknowledged → COMMENT.

Warning (from GPT-5.6 Sol, parent-verified)

Stale files on re-export to the same directory (export.py:139)

If export_package is called with weights_dir and then re-called without it on the same out_dir, the stale weights/ directory remains while manifest.json reports has_weights: false. Consider exporting into a clean/staged directory or clearing out_dir before writing.

Non-blocking suggestions (from Claude Opus)

  • [suggestion] export.py:310LGBMRegressor(random_state=42, verbose=-1, **HYPERPARAMETERS) will raise TypeError if HYPERPARAMETERS contains random_state or verbose keys (duplicate keyword argument).
  • [nit] export.py:268 — Generated _workflow() gates api_key on SOURCE in ("allora", "atlas") but ModelSpec.validate() only permits "binance" and "allora". The "atlas" branch is dead code.

(Reviewed by Helder's Agent — GPT-5.6 Sol + GLM 5.2 + Kimi K3 + Claude Opus 4.8)

@helder-moreira

Copy link
Copy Markdown
Author

Addressed the aggregated review findings. Triage:

  1. [warning] stale files on re-export (export.py:139) — already-fixed. Verified present on this branch (6b79248): export_package unconditionally shutil.rmtrees weights/ before optionally recopying, so a re-export without weights_dir can't leave a stale dir behind has_weights=false. Covered by test_reexport_without_weights_clears_stale_weights. No change needed.

  2. [suggestion] duplicate-kwarg crash in generated train_model — valid. Fixed in bc0aa9d. The generated model now builds params = {"random_state": 42, "verbose": -1, **HYPERPARAMETERS} and calls LGBMRegressor(**params), so user-supplied random_state/verbose win without TypeError: got multiple values for keyword argument. Added regression test test_hyperparameters_can_override_defaults_without_duplicate_kwargs.

  3. [nit] dead atlas branch — valid. Fixed in bc0aa9d. ModelSpec.validate() only permits binance|allora, so the generated _workflow() gate is now if SOURCE == "allora" (dropped the unreachable "atlas"). Updated test_api_key_not_passed_unconditionally accordingly.

Validated the export logic via a stdlib-only importlib harness (params merge resolves correctly, atlas fully removed) and py_compile on the generated model.py. Backwards-compatible; --force-with-lease not needed (normal push).

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grok 4.5 Review — REQUEST_CHANGES

Shared engineered-features extraction is correct (index/guard/eps parity, safe column names, both train/serve paths + solid offline parity tests). Blocked on export package bug: LGBMRegressor construction lets fixed kwargs collide with user hyperparameters.

(Grok 4.5 — single-model review)

Comment thread allora_forge_builder_kit/export.py Outdated
Comment thread allora_forge_builder_kit/export.py
Comment thread allora_forge_builder_kit/export.py Outdated
@helder-moreira
helder-moreira marked this pull request as ready for review July 21, 2026 17:09

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files

Architecture diagram
sequenceDiagram
    participant User
    participant CLI as allora-forge-export CLI
    participant Export as export_package()
    participant FS as File System (output dir)
    participant Model as forge_model/model.py
    participant EF as apply_engineered_features()
    participant NB as Training Notebook
    participant Platform as Hosting Platform

    Note over User,FS: EXPORT TIME

    User->>CLI: allora-forge-export --config model.json --out ./pkg [--weights ./w]
    CLI->>Export: Export package with ModelSpec
    Export->>FS: Write forge_model/ (model.py, config.json, __init__.py)
    Export->>FS: Write pyproject.toml (entry-point, builder-kit ref)
    Export->>FS: Write manifest.json (model-only: model_type, supports_training, code_hash)
    alt With weights
        Export->>FS: Copy weights/ directory
    end
    Export->>FS: Write .dockerignore
    Export-->>CLI: Package path
    CLI-->>User: exported to ./pkg (zip & upload)

    Note over NB,Platform: RUNTIME: TRAINING PATH

    NB->>EF: apply_engineered_features(df, ENGINEERED_SPECS, N_INPUT_BARS)
    Note over NB,EF: NEW: shared module replaces inline engineer_returns()
    EF-->>NB: (df with engineered columns, added column names)

    Note over Model,Platform: RUNTIME: DEPLOYMENT (SERVING / TRAIN-ON-PLATFORM)

    Platform->>Model: Run model (train/predict)
    Model->>Model: Read PAIR, TIMEFRAME from env
    Model->>Model: Load config.json (engineered_specs, hyperparams, etc.)
    Model->>EF: apply_engineered_features(live_df, ENGINEERED_SPECS, N_INPUT_BARS)
    Note over Model,EF: Same function, same spec — no train/serve skew
    EF-->>Model: (df with features)
    opt Training on platform
        Model->>Model: train_model() → LGBMRegressor.fit()
    end
    Model->>Model: predict() → return float
    Model-->>Platform: prediction value
Loading

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread allora_forge_builder_kit/export.py
Comment thread allora_forge_builder_kit/export.py
Comment thread allora_forge_builder_kit/workflow.py
Comment thread allora_forge_builder_kit/engineered_features.py
Comment thread allora_forge_builder_kit/engineered_features.py Outdated
Comment thread notebooks/Allora Forge Builder Kit.ipynb Outdated
Comment thread allora_forge_builder_kit/export.py Outdated
Comment thread notebooks/example_topic_77_bitcoin_5min_walkthrough.py
Comment thread notebooks/Allora Forge Builder Kit.ipynb Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread allora_forge_builder_kit/engineered_features.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread allora_forge_builder_kit/engineered_features.py Outdated
@clementupshot

Copy link
Copy Markdown

Ran this end-to-end locally: 157 passed / 0 failed on the branch, export exercised in both code-only and --weights modes plus negative cases. Output matches the description — manifest carries no pair/timeframe/topic, model.py reads PAIR/TIMEFRAME from env, code_hash is deterministic and excludes weights, and a re-export clears stale weights/. The skew claim checks out too: walkthroughs and notebook now call the shared apply_engineered_features (inline code deleted, not copied) and the consistency tests prove parity. Branch contains all of main, merges clean.

A few things worth fixing, none blocking:

  1. Manifest XOR trap vs forge-v2#260. Forge rejects packages unless exactly one of supports_training / has_weights is true, but export doesn't enforce that: --weights without --no-training emits both true, and --no-training without --weights emits both false (warning only, export.py:206). Both get bounced at upload. Validating at export time would surface this here instead of at forge.
  2. Generated pyproject installs builder-kit from git+…@main (default builder_kit_ref, export.py:119). Until this PR merges, main has neither export.py nor engineered_features.py, so a package exported from this branch ImportErrors at serve time. It's also a moving ref — a pinned sha/tag default would make train/serve reproducible.
  3. Malformed configs leak raw tracebacks. main() catches ValueError/OSError/JSONDecodeError (export.py:213), but engineered_specs as a string hits an AttributeError in validate (export.py:78) and a missing required key hits a TypeError in from_dict (export.py:97). Fails closed either way, just noisy; catching those two restores the clean export failed: path.
  4. Nothing produces the upload zip. Forge extracts flat and reads manifest.json at the root, so zipping the out dir itself (instead of its contents) fails with a confusing "manifest not found". A --zip flag or a one-liner in the usage docs would spare that.

@jefferythewind

Copy link
Copy Markdown
Collaborator

Under Review.

@helder-moreira

Copy link
Copy Markdown
Author

Thanks @clementupshot — addressed in 40de7f8.

1. Manifest XOR — fixed. export_package now hard-errors unless exactly one of supports_training/has_weights is true, mirroring forge's hosting/manifest.go message, so both-true (--weights while trainable) and both-false surface at export instead of at upload. Removed the now-redundant both-false warning in main(). Added tests for both rejected combos.

3. Malformed configs — fixed. engineered_specs as a non-list (or a list of non-objects) now raises a clean ValueError in validate() instead of an AttributeError, and a missing required key is re-raised from from_dict as ValueError instead of a TypeError — both restore the export failed: path. Covered by tests, including a main()-level check.

4. Upload zip — added. New --zip flag writes <out>.zip with the package contents at the archive root (so forge finds manifest.json at the extraction root, not nested). Test asserts the flat layout.

2. git+…@main ref — leaving as-is, with a doc tweak. The pre-merge ImportError resolves the moment this PR lands on main (which is the correct default thereafter), and --builder-kit-ref already lets you pin a sha/tag per export — defaulting to a pinned sha would mean hand-editing it every release. I updated the flag's help text to recommend pinning a sha/tag for reproducible production serving. Happy to reconsider if you'd rather main not be the default.

@jefferythewind jefferythewind left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall: The shared `engineered_features` module is a clean, well-tested solution to the train/serve skew problem, and the `apply_engineered_features` refactor in the walkthroughs is the right call. Two issues worth resolving before merging:


1. No user-facing documentation or example for the export path

The README has no mention of `export_package`, `ModelSpec`, `engineered_specs`, `apply_engineered_features`, or `allora-forge-export`. A user who installs this package has no way to discover the export workflow or know what a valid `model.json` looks like without reading the source or the test file.

The walkthroughs (`example_topic_69`, `example_topic_77`) are still local-deployment examples — that's correct, don't change them. But there should be at minimum a dedicated example (e.g. `notebooks/export_to_hosting.py`) walking through the full export path: build a `ModelSpec`, call `export_package`, understand what the output directory contains.

Beyond that example, the README needs to explain how the hosted worker actually behaves at runtime — none of this is currently documented anywhere:

  • Default behavior (train-on-platform, `supports_training=True`): The platform trains on first run and on schedule. What does the worker serve before the first training run completes? What triggers retraining — a time schedule, a platform event? How often?
  • Train-locally mode (`supports_training=False` + `--weights`): The platform never retrains; it only serves the bundled weights. What happens if weights go stale? Can weights be updated without re-exporting?
  • The `--no-training` flag and `supports_training` field: Their relationship to each other and to the SDK's `TrainingRunner` needs explaining — right now the only hint is a comment in the source code.
  • Environment variables the hosted worker depends on (`PAIR`, `TIMEFRAME`, `ALLORA_API_KEY`, `SUBMIT_RETURNS`, `DATA_BASE_PATH`) — none are documented for the user configuring a deployment.

The README file map and API quick reference should also surface `engineered_features.py` and `export.py`.


2. Architecture: this should live in `WorkerManager`, not as a standalone CLI

Introducing a separate `allora-forge-export` CLI creates two parallel deployment workflows with no shared interface. The `WorkerManager` already owns the "deploy to Allora" mental model for users of this kit. The right shape is one system, two deployment types — local and cloud — both managed through `WorkerManager`.

Concretely:

  • `WorkerManager` should expose a method like `deploy_hosted(spec, ...)` that calls `export_package` internally and handles the upload to forge
  • The monitoring story is largely already there — most worker health signals come from the chain and `WorkerMonitor` picks them up regardless of whether the worker runs locally or in a hosted container. The only methods that need explicit local/cloud branching are the container-specific ones: log tailing and heartbeat/liveness. Those should be made deployment-type-aware rather than silently only working for local workers
  • This gives cloud-deployed workers the same monitoring transparency users already get locally: submission history, on-chain scores, inference values, and worker health all in one place

Keeping `export_package` and `ModelSpec` as library-level primitives is fine — they're the right level of abstraction. But the user-facing entry point should be `WorkerManager`, not a separate CLI that users have to discover independently.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread allora_forge_builder_kit/export.py
@helder-moreira

Copy link
Copy Markdown
Author

Thanks @jefferythewind — clear and fair.

1. Docs + export example — agreed, doing it now. I'll add a notebooks/export_to_hosting.py walking the full path (build a ModelSpecexport_package → what the output dir contains), plus README sections covering: the export workflow, train-on-platform vs train-locally behavior, the --no-training/supports_training relationship to the SDK's TrainingRunner, and the deployment env vars (PAIR, TIMEFRAME, ALLORA_API_KEY, SUBMIT_RETURNS, DATA_BASE_PATH). I'll surface engineered_features.py and export.py in the file map and API quick reference. I'm verifying the runtime-behavior specifics against the worker SDK so the docs state facts, not guesses.

2. WorkerManager integration — I'd like to align on scope before writing code. I agree the primitives (export_package/ModelSpec) are at the right level, and that 'one system, two deployment types' is the right end-state for the user-facing entry point. My hesitation is scope: this PR is phase0 (shared features + the export primitives), and WorkerManager.deploy_hosted(spec, ...) pulls in a forge upload path (endpoint + auth contract) and deployment-type-aware monitoring (log tail / heartbeat branching) that feels like its own phase.

Two questions so I build the right thing:

  • Is the forge upload contract (endpoint, auth, and whether it's POST /api/v1/models with a zipped package) settled enough to build against now, or still in flux?
  • Would you be open to landing this PR as the library layer and doing deploy_hosted + deployment-aware monitoring as an immediate follow-up, or do you consider the WorkerManager entry point a merge blocker for feat(hosting): shared engineered-features module + model package export #40?

Happy to go either way — just want to avoid building against an unsettled upload path. (The docs in #1 will describe the CLI export flow as it stands; I'll update them when the WorkerManager entry point lands.)

@helder-moreira

Copy link
Copy Markdown
Author

#1 done in 3ccfe2b: added notebooks/export_to_hosting.py (build ModelSpecexport_package → inspect the output dir), and README now has a Deploy to the hosting platform (export) section covering the two modes and the exactly-one supports_training/weights rule, the runtime behavior (train-on-platform runs a scheduled allora-worker train job with a 12h freshness skip; no artifact ⇒ inference raises until the first train completes; train-locally never retrains and serves bundled weights), and the deploy-time env vars (PAIR/TIMEFRAME/ALLORA_TOPIC_ID/ALLORA_API_KEY/SUBMIT_RETURNS/DATA_BASE_PATH). engineered_features.py and export.py are now in the file map + API quick reference. Runtime claims were verified against the worker SDK. Still keen on your take on the #2 scope questions above.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="notebooks/export_to_hosting.py">

<violation number="1" location="notebooks/export_to_hosting.py:92">
P2: The upload step printed by this walkthrough cannot be followed after the preceding `export_package` call: this script never writes `model.json`, so `allora-forge-export` fails while reading the required `--config` path. The example could serialize the in-memory `spec` before invoking the CLI, or zip the already-created package contents directly.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:221">
P3: The example's model-type constraint allows names such as `_my_model` or `-my-model` that `export_package` rejects. Matching the documented pattern to the validator would make invalid configuration failures predictable.</violation>

<violation number="2" location="README.md:239">
P2: Train-locally packages cannot generally be moved to arbitrary pairs/timeframes because their bundled artifact is tied to the training parameters. Qualifying this sentence to code-only/train-on-platform packages would prevent users from deploying incompatible weights.</violation>

<violation number="3" location="README.md:259">
P1: A deployment that follows these documented defaults can fail before training or inference: the generated worker requires both variables, and Binance cannot parse `5min` as an interval. The table would be accurate if it marked both variables required and used Binance's `5m` spelling.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread README.md
Comment on lines +259 to +260
| `PAIR` | Trading pair, e.g. `BTCUSD` | `ETHUSD` |
| `TIMEFRAME` | Bar interval, e.g. `5min`, `1h` | `5min` |

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: A deployment that follows these documented defaults can fail before training or inference: the generated worker requires both variables, and Binance cannot parse 5min as an interval. The table would be accurate if it marked both variables required and used Binance's 5m spelling.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 259:

<comment>A deployment that follows these documented defaults can fail before training or inference: the generated worker requires both variables, and Binance cannot parse `5min` as an interval. The table would be accurate if it marked both variables required and used Binance's `5m` spelling.</comment>

<file context>
@@ -205,6 +206,65 @@ Mainnet topics and their testnet equivalents:
+
+| Env var | Purpose | Default |
+|---------|---------|---------|
+| `PAIR` | Trading pair, e.g. `BTCUSD` | `ETHUSD` |
+| `TIMEFRAME` | Bar interval, e.g. `5min`, `1h` | `5min` |
+| `ALLORA_TOPIC_ID` | Target topic | `69` |
</file context>
Suggested change
| `PAIR` | Trading pair, e.g. `BTCUSD` | `ETHUSD` |
| `TIMEFRAME` | Bar interval, e.g. `5min`, `1h` | `5min` |
| `PAIR` | Trading pair, e.g. `BTCUSD` | required |
| `TIMEFRAME` | Bar interval, e.g. `5m`, `1h` | required |
Fix with cubic

# manifest.json at the extraction root) and upload to forge. The CLI can do the
# zip for you:
#
# allora-forge-export --config model.json --out build/my_lgbm_package --zip

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The upload step printed by this walkthrough cannot be followed after the preceding export_package call: this script never writes model.json, so allora-forge-export fails while reading the required --config path. The example could serialize the in-memory spec before invoking the CLI, or zip the already-created package contents directly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At notebooks/export_to_hosting.py, line 92:

<comment>The upload step printed by this walkthrough cannot be followed after the preceding `export_package` call: this script never writes `model.json`, so `allora-forge-export` fails while reading the required `--config` path. The example could serialize the in-memory `spec` before invoking the CLI, or zip the already-created package contents directly.</comment>

<file context>
@@ -0,0 +1,96 @@
+#    manifest.json at the extraction root) and upload to forge. The CLI can do the
+#    zip for you:
+#
+#       allora-forge-export --config model.json --out build/my_lgbm_package --zip
+#
+#    then upload build/my_lgbm_package.zip via forge (POST /api/v1/models).
</file context>
Fix with cubic

Comment thread README.md
# then upload build/my_lgbm_package.zip to forge (POST /api/v1/models)
```

`--zip` writes the package **contents** at the archive root, so forge finds `manifest.json` at the extraction root. The generated worker is **generic over pair/timeframe** — one package can be deployed against many pairs/timeframes/topics.

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Train-locally packages cannot generally be moved to arbitrary pairs/timeframes because their bundled artifact is tied to the training parameters. Qualifying this sentence to code-only/train-on-platform packages would prevent users from deploying incompatible weights.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 239:

<comment>Train-locally packages cannot generally be moved to arbitrary pairs/timeframes because their bundled artifact is tied to the training parameters. Qualifying this sentence to code-only/train-on-platform packages would prevent users from deploying incompatible weights.</comment>

<file context>
@@ -205,6 +206,65 @@ Mainnet topics and their testnet equivalents:
+# then upload build/my_lgbm_package.zip to forge (POST /api/v1/models)
+```
+
+`--zip` writes the package **contents** at the archive root, so forge finds `manifest.json` at the extraction root. The generated worker is **generic over pair/timeframe** — one package can be deployed against many pairs/timeframes/topics.
+
+### Two deployment modes
</file context>
Suggested change
`--zip` writes the package **contents** at the archive root, so forge finds `manifest.json` at the extraction root. The generated worker is **generic over pair/timeframe** — one package can be deployed against many pairs/timeframes/topics.
`--zip` writes the package **contents** at the archive root, so forge finds `manifest.json` at the extraction root. The generated worker code is **generic over pair/timeframe**; code-only packages can be deployed against many pairs/timeframes/topics, while bundled-weight packages must use matching deployment parameters.
Fix with cubic

Comment thread README.md
# Model-INTRINSIC config (baked into the package's config.json). Pair/timeframe/
# topic are NOT here — they are chosen per deployment (see env vars below).
spec = ModelSpec(
model_type="my_lgbm", # entry-point name; [a-z0-9_-]

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The example's model-type constraint allows names such as _my_model or -my-model that export_package rejects. Matching the documented pattern to the validator would make invalid configuration failures predictable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 221:

<comment>The example's model-type constraint allows names such as `_my_model` or `-my-model` that `export_package` rejects. Matching the documented pattern to the validator would make invalid configuration failures predictable.</comment>

<file context>
@@ -205,6 +206,65 @@ Mainnet topics and their testnet equivalents:
+# Model-INTRINSIC config (baked into the package's config.json). Pair/timeframe/
+# topic are NOT here — they are chosen per deployment (see env vars below).
+spec = ModelSpec(
+    model_type="my_lgbm",                          # entry-point name; [a-z0-9_-]
+    engineered_specs=[{"kind": "log_return", "window_bars": 6}],
+    number_of_input_bars=24,
</file context>
Suggested change
model_type="my_lgbm", # entry-point name; [a-z0-9_-]
model_type="my_lgbm", # entry-point name; [a-z0-9][a-z0-9_-]*
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants