Commit graph

175 commits

Author SHA1 Message Date
5a363f3b6c fix(video): add torchvision to video-marlin extras
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Missing from initial extras list — required by QwenVLVideoProcessor
at inference time. On CUDA 13 nodes must be installed from the PyTorch
nightly cu130 index to avoid a torch version downgrade:
  pip install --index-url https://download.pytorch.org/whl/nightly/cu130 torch torchvision

Discovered during Muninn deployment (2026-05-26).
2026-06-02 20:32:03 -07:00
a7d916f630 docs: add LLM development disclosure to README
Some checks failed
CI / test (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
Humans own design, architecture, code review, testing, and
verification. LLMs are part of our development workflow.
Links to circuitforge.tech/positions for our full position.
2026-05-28 08:20:17 -07:00
c2ac55259d fix(video): enforce PCI_BUS_ID order + force CUDA_VISIBLE_DEVICES assignment
Some checks failed
CI / test (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
CUDA defaults to FASTEST_FIRST device ordering, which does not match
nvidia-smi's PCI bus order on multi-GPU nodes. On Muninn, the RTX 3090
is cuda:0 and the Quadro RTX 4000 is cuda:1 — the opposite of nvidia-smi.

Two fixes:
1. Set CUDA_DEVICE_ORDER=PCI_BUS_ID so --gpu-id always matches nvidia-smi
   and the muninn.yaml profile GPU index assignments.
2. Use direct assignment (os.environ[...] = ...) instead of setdefault —
   setdefault silently no-ops if CUDA_VISIBLE_DEVICES is already present
   in the environment (conda activation, prior run, system default).
2026-05-26 15:07:30 -07:00
9f7fb45071 feat(video): add cf-video module — Marlin-2B FastAPI service + mock backend + tests
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Add the circuitforge_core.video package implementing the cf-video inference
service managed by cf-orch.

Service endpoints:
  GET  /health     — liveness check; model name + VRAM
  POST /caption    — dense scene description + timestamped event list
  POST /find       — temporal grounding of a natural-language event query

Backend hierarchy:
  VideoBackend (Protocol)
    MarlinBackend  — NemoStation/Marlin-2B via transformers>=5.7.0
    MockVideoBackend — deterministic stub; no GPU required

Pydantic request/response models enforce parameter bounds at the API
boundary (max_new_tokens ge/le, event min_length=1).  Span is serialized
as list[float] | None for JSON compatibility.

MarlinBackend loads eagerly in __init__ so cf-orch's 2-second liveness
poll catches load failures immediately.  FORCE_QWENVL_VIDEO_READER env var
defaults to torchcodec (faster than av path) before transformers import.

pyproject.toml extras:
  video-marlin   — torch, transformers, torchcodec, qwen-vl-utils, av, Pillow
  video-service  — video-marlin + fastapi + uvicorn

Test coverage: 46 tests across test_mock_backend.py and test_app.py.
All passing without GPU or real video file.

Closes: #71
2026-05-25 20:00:37 -07:00
93d36346c1 feat(llm): task-based cf-orch allocation in LLMRouter (v0.21.0)
_try_cf_orch_alloc now checks for cf_orch.task + cf_orch.product keys.
When present, uses client.task_allocate(product, task) instead of
service-based allocate(). Supports peregrine#115 task-model routing.
Existing service-based configs are unaffected.
2026-05-17 19:59:48 -07:00
af66877b51 feat(community): recipe dedup support — similar_to_ref FK, search_similar_posts, migration 006
Some checks failed
CI / test (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
Adds three-layer dedup infrastructure for community recipe posts:
- Migration 006: similar_to_ref self-FK, title lower() index, recipe_id index
- CommunityPost.similar_to_ref optional field (frozen dataclass, defaults None)
- SharedStore.search_similar_posts(): title ILIKE + recipe_id match, ordered by relevance
- insert_post() wires similar_to_ref into the INSERT
2026-05-11 17:09:18 -07:00
41c9830281 docs(readme): landing page rewrite — v0.20.0, all 28 modules, LLM router + DB usage examples, full extras install table, used-by table
Some checks failed
CI / test (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
2026-05-06 08:51:54 -07:00
fb3a4c697d feat(llm): v0.20.0 — LLMRouter dict init + Ollama embed preflight (closes #59, #60)
Some checks failed
CI / test (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
- LLMRouter.__init__ now accepts a Path | dict; pagepiper ingest scripts
  pass a runtime-constructed config dict instead of a temp file
- _check_ollama_model_pulled() preflight on embed(): checks /api/tags once
  per backend URL and raises RuntimeError("...Fix: ollama pull <model>")
  when the configured embedding model is not pulled; silently skips for
  non-Ollama backends (vLLM, etc.) that don't expose /api/tags
- 6 new tests: dict init paths (x2) + preflight scenarios (x4)
- Existing embed tests updated to mock requests.get to avoid live Ollama calls
2026-05-05 14:59:49 -07:00
ccc6a15d94 feat: cf-core v0.19.0 — add PDF extraction, VectorStore, LLMRouter.embed()
Some checks failed
CI / test (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
2026-05-04 16:11:57 -07:00
0ddb3cbf07 chore: bump cf-core to v0.19.0 (add pdf, vector, llm.embed) 2026-05-04 16:04:48 -07:00
7526092481 fix(llm): strengthen embed skip-verification test; add DEMO_MODE check to embed() 2026-05-04 16:02:26 -07:00
8e2d15bcd4 feat(llm): add LLMRouter.embed() for batch embedding generation
Adds embed(texts, model_override, fallback_order) to LLMRouter. Only
openai_compat backends are tried (Ollama/vLLM expose /v1/embeddings;
anthropic and vision_service do not). Uses embedding_model from backend
config when present, falls back to the chat model otherwise. Supports
cf-orch allocation and raises RuntimeError when all backends are exhausted.

4 tests added (TDD: RED → GREEN), 763 total passing, no regressions.
2026-05-04 15:58:44 -07:00
a6d906bcbb fix(vector): explicit rollback, table identifier guard, query scope fix 2026-05-04 15:55:05 -07:00
0489f1111c feat(vector): add LocalSQLiteVecStore backed by sqlite-vec
Implements the VectorStore ABC using sqlite-vec virtual tables.
Two-table design (vec0 virtual + companion meta) supports upsert,
top-k ANN query with optional metadata post-filter, delete by ID,
and bulk delete_where. Also renames VectorMatch.id → entry_id to
avoid shadowing the Python builtin, updating base.py and all tests.

Installed: sqlite-vec 0.1.9
Tests: 16 passed (7 base + 9 integration)
2026-05-04 15:41:39 -07:00
e6c69f25ae fix(vector): rename VectorMatch.entry_id to id per downstream contract
VectorMatch.entry_id renamed to VectorMatch.id to match the API contract
expected by downstream consumers (pagepiper T7). The dataclass remains frozen
to prevent field reassignment; metadata is kept as plain dict for JSON
deserialization compatibility.

- Renamed VectorMatch.entry_id field to id
- Updated all test references to use .id accessor
- Simplified metadata to plain dict (removed MappingProxyType wrapping)
- All 7 tests passing
2026-05-04 14:19:14 -07:00
9492942623 fix(vector): make VectorMatch.metadata immutable; rename id to entry_id 2026-05-04 11:46:24 -07:00
fe51914902 feat(vector): add VectorStore ABC and VectorMatch dataclass 2026-05-04 11:42:03 -07:00
ac45067ae7 test(documents): add OCR fallback and edge case tests for PDFExtractor 2026-05-04 08:45:53 -07:00
408ab64c55 test(documents): add OCR and ImportError coverage for PDFExtractor
- Add module-level guards for pytesseract and PIL.Image (enables patching in tests)
- Move `import io` from inside _ocr_page to module-level stdlib imports
- Extract _ensure_pil_image() helper with TypeError guard so isinstance check
  does not blow up when Image is patched to a MagicMock in tests
- Add 3 new tests: pdfplumber=None ImportError, sparse-page OCR fallback,
  OCR render failure returns empty chunk
- Coverage: 96% (up from 64%)
2026-05-04 08:39:31 -07:00
bbb146b361 feat(documents): add PDFExtractor text-layer extraction and PageChunk
Adds circuitforge_core/documents/pdf.py with:
- PageChunk frozen dataclass (page_number, text, source, word_count)
- PDFExtractor.chunk_pages() — pdfplumber text-layer per page, OCR fallback via pytesseract for sparse pages
- Module-level graceful ImportError guard on pdfplumber (patchable, follows cf-core optional-extra pattern)
- pdf and pdf-ocr optional extras declared in pyproject.toml

3 tests, all passing.
2026-05-04 08:33:10 -07:00
3be21ce452 chore: gitignore .worktrees directory 2026-05-04 08:23:39 -07:00
73f694ed3a fix(input/gestures): restore Iterator[np.ndarray] return type on frames() 2026-04-26 20:48:50 -07:00
0f5ea86ab0 fix(input/gestures): enforce numpy array immutability in HandLandmarks; add CameraCapture tests
- Set points.flags.writeable = False in HandsDetector.detect() so in-place
  mutation of HandLandmarks.points raises ValueError (frozen=True alone does not
  protect numpy array contents)
- Extend test_handlandmarks_is_immutable to assert ValueError on array mutation
- Add test_camera.py with 3 tests covering is_open, frames() yield/break
  behaviour, and context manager release (was at 0% coverage)
- Remove unused `import numpy as np` from camera.py; fix frames() return
  annotation to Iterator (np.ndarray ref removed with the import)
2026-04-26 20:48:02 -07:00
cb3d186a58 chore: bump cf-core to v0.18.0 — adds cf_input.gestures module
Some checks failed
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
2026-04-26 20:20:28 -07:00
a62bff5f1e test(input/gestures): add full pipeline smoke test 2026-04-26 20:18:40 -07:00
524cc62812 feat(input/gestures): add CameraCapture and public __init__ exports 2026-04-26 20:16:18 -07:00
a31e6099c6 feat(input/gestures): implement HandsDetector wrapping mediapipe Hands 2026-04-26 20:08:05 -07:00
5a4917d455 style: black format normalizer.py and test_normalizer.py 2026-04-26 20:05:54 -07:00
460530bb03 feat(input/gestures): implement normalize_hand() with scale/translation invariance 2026-04-26 19:58:00 -07:00
b2b58913c7 feat: scaffold cf_input.gestures module + gestures-mediapipe dep group 2026-04-26 18:51:45 -07:00
185057d8ca feat(reranker): full adapter suite + cf-orch auto-routing (closes #54)
Some checks failed
CI / test (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
Five backends: BGE (FlagEmbedding), Qwen3 (generative yes/no logit scorer,
batched forward pass), CrossEncoder (sentence-transformers, covers mxbai-rerank
/ ms-marco / jina), Cohere (BYOK cloud), Remote (HTTP delegate to cf-reranker
service). Mock adapter for tests. 54 tests.

cf-reranker FastAPI service app (port 8011) — cf-orch manages as a process,
defaults to Qwen3-Reranker-0.6B.

make_reranker() auto-detects CF_ORCH_URL and routes to cf-orch cf-reranker
when set — cloud apps (Kiwi, Peregrine, Snipe) get remote Qwen3 reranking
with zero code changes. Local dev falls back to local BGE.

pyproject extras: reranker-bge, reranker-qwen3, reranker-cross-encoder,
reranker-cohere, reranker-service.
2026-04-26 09:04:39 -07:00
b21d6acc8e feat: add detect_byok() public utility to cloud_session, bump v0.16.1
Some checks failed
CI / test (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
Extracted from kiwi/avocet where it was duplicated. Reads llm.yaml via
the same path LLMRouter uses — products can now import detect_byok from
cf-core instead of maintaining their own copy.
2026-04-25 16:01:05 -07:00
b52c578911 chore: bump version to 0.16.0 — cloud_session shared module release
Some checks failed
CI / test (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
2026-04-24 17:29:00 -07:00
00737d22cf feat(cloud_session): shared cloud session resolution for all CF products
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Extracts the JWT validation + Heimdall tier resolution + guest session pattern
that was duplicated across kiwi and peregrine into a single reusable module.

CloudSessionFactory is parameterized by product name. Products instantiate it
once at module level and call .dependency() to get a FastAPI-compatible Depends()
function. .require_tier(min_tier) returns a dependency factory for gated routes.

CloudUser carries:
  user_id   — Directus UUID, "local" (self-hosted), "local-dev" (bypass), "anon-<uuid>"
  tier      — free | paid | premium | ultra | local
  product   — which CF product this session is for
  has_byok  — whether user has a configured LLM backend
  meta      — dict for product-specific extras (household_id, license_key, etc.)

Products can pass extra_meta= to attach product-specific fields without
subclassing. The module is FastAPI-only (fastapi is a lazy import so local-mode
products that never hit cloud paths don't pay the import cost).
2026-04-24 16:39:27 -07:00
383897f990 feat: platforms module + docs + scripts
Some checks failed
CI / test (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
- platforms/: eBay platform adapter (snipe integration layer)
- docs/: developer guide, module reference, getting-started docs
- scripts/: utility scripts for development and deployment
2026-04-24 15:23:16 -07:00
8b357064ce feat(musicgen): cf-musicgen module — MusicGen inference server
FastAPI service wrapping facebook/musicgen-* models.
Exposes POST /generate {prompt, duration_s} → audio/wav.
Registered in VRAM tiers (8GB+).
2026-04-24 15:23:09 -07:00
146fe97227 feat(text): ollama + vllm proxy backends for cf-text
- backends/ollama.py: routes requests to a running Ollama instance via HTTP API
- backends/vllm.py: routes requests to vllm's OpenAI-compatible API
  (/v1/chat/completions); cf-text holds no GPU memory in proxy mode
2026-04-24 15:23:02 -07:00
8d9b55ef8f chore: add cf-musicgen to VRAM tier lists + minor tts type fixes
- hardware/tiers.py: register cf-musicgen in 8GB, 16GB, and 32GB VRAM tiers
- tts/app.py: use inline type comment for _backend to avoid runtime global warning
- tts/backends/base.py: minor style cleanup
2026-04-24 14:07:01 -07:00
05063c2619 feat(text): multi-GPU spanning via --gpu-ids + CUDA_VISIBLE_DEVICES
- create_app: add gpu_ids param; when set, exports CUDA_VISIBLE_DEVICES=<ids>
  so HuggingFace Accelerate auto-shards across all listed devices
- CLI: add --gpu-ids arg (e.g. "0,1"); overrides --gpu-id when provided
- backends/base.py: propagate gpu_ids through TextBackend.generate
  so backends can be aware of the visible device set

Single-GPU deployments are unaffected — --gpu-id=0 remains the default.
2026-04-24 14:06:53 -07:00
f2ae43696b feat(community): recipe_tags + tag vote tables and store methods
Adds community subcategory tagging for corpus recipes (kiwi#118).
Any product with a recipe corpus can use this to let users tag recipes
into browse taxonomy locations that FTS missed.

- 005_recipe_tags.sql: recipe_tags (per-recipe taxonomy tag with upvote
  counter) + recipe_tag_votes (dedup table; submitter self-vote at insert)
- store.py: submit_recipe_tag(), upvote_recipe_tag(), get_recipe_tag_by_id(),
  list_tags_for_recipe(), get_accepted_recipe_ids_for_subcategory()

Acceptance threshold: upvotes >= 2 (submitter counts as 1, one more needed).
Tags keyed as recipe_source='corpus' for future community-recipe extension.
2026-04-22 12:32:40 -07:00
82f0b4c3d0 feat: cf_core.reranker — shared reranker module Phase 1 (#54)
Some checks failed
CI / test (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
Trunk + text branch + BGE adapter:
- base.py: Reranker Protocol, RerankResult (frozen dataclass), TextReranker
  base class with rerank() / rerank_batch() built on _score_pairs()
- adapters/mock.py: MockTextReranker — Jaccard scoring, no deps, deterministic
- adapters/bge.py: BGETextReranker — FlagEmbedding cross-encoder, thread-safe,
  batched forward pass via rerank_batch(); graceful ImportError if dep missing
- __init__.py: rerank() singleton, make_reranker(), reset_reranker();
  CF_RERANKER_MODEL / CF_RERANKER_BACKEND / CF_RERANKER_MOCK env vars
- pyproject.toml: reranker-bge and reranker-qwen3 optional dep groups
- 20 tests, all passing

Architecture ready for Phase 2 (Qwen3TextReranker) and Phase 3 (cf-orch remote
backend). ImageReranker/AudioReranker branches stubbed in base.py docstring.
2026-04-21 12:25:01 -07:00
3167ee8011 docs(llm/router): document backend types and trunk services in module docstring (closes #53)
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Names cf-text, cf-voice, cf-vision as trunk services with the cf_orch
allocation block pattern. Documents all backend types (openai_compat,
anthropic, vision_service) and the env-var auto-detection path.
2026-04-20 13:23:45 -07:00
1553ff1630 feat: add activitypub module — actor, objects, signing, delivery, Lemmy, inbox (closes #51)
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
CFActor (frozen dataclass, RSA keygen), AS2 object constructors (Note, Offer,
Request, Create), HTTP Signatures (draft-cavage-http-signatures-08, rsa-sha256),
signed delivery via requests, Lemmy REST client (JWT auth), FastAPI inbox router
with optional signature verification.

Digest header re-verified against actual body bytes on verify_signature() to
prevent body-swap attacks. inbox.py omits __future__ annotations to avoid
FastAPI's annotation-resolution-against-module-globals constraint.

105 tests. Bumps to v0.14.0.
2026-04-20 13:18:03 -07:00
f9b9fa5283 feat: add currency_code preference + format_currency utility (closes #52)
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Adds circuitforge_core.preferences.currency with get/set_currency_code()
and format_currency(). Priority chain: store → CURRENCY_DEFAULT env → USD.
Formatting uses babel when available; falls back to a 30-currency symbol
table with correct ISO 4217 minor-unit decimal places (0 for JPY, KRW, etc.).
Consumed by Snipe, Kiwi, Peregrine, Crossbill. Bumps to v0.13.0.
2026-04-20 13:06:04 -07:00
aa057b20e2 feat: add job_quality deterministic trust scorer (closes #48)
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
12 signal functions covering staleness, repost patterns, salary transparency,
ATS blackhole detection, and enrichment signals. All pure functions — no LLM,
no network, no I/O. trust_score = 1 - sum(triggered weights), clamped to [0,1].
confidence reflects fraction of signals with available evidence.

Salary transparency enforced for CO/CA/NY/WA/IL/MA. ATS blackhole patterns:
Lever, Greenhouse, Workday, iCIMS, Taleo.

83 tests (models, all 12 signals individually, scorer). Bumps to v0.12.0.
2026-04-20 13:02:57 -07:00
97ab3bac85 chore: sync __version__ to 0.11.0
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
2026-04-20 11:29:58 -07:00
90e60f8965 chore: bump version to 0.11.0
Some checks failed
CI / test (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release — PyPI / release (push) Has been cancelled
2026-04-20 11:19:09 -07:00
80eeae5460 feat: audio module, musicgen tests, SQLCipher PRAGMA hardening
#45 — db/base.py: PRAGMA key=? parameterized form instead of f-string
interpolation. Regression tests added (skip when pysqlcipher3 absent).

#50 — circuitforge_core.audio: shared PCM/signal utilities (MIT, numpy-only)
  - convert.py: pcm_to_float32, float32_to_pcm, bytes_to_float32
  - gate.py:    is_silent, rms (RMS energy gate)
  - resample.py: resample (scipy.signal.resample_poly; numpy linear fallback)
  - buffer.py:  ChunkAccumulator (window-based chunk collector + flush)
Replaces hand-rolled equivalents in cf-voice stt.py + context.py.
34 tests, all passing.

#49 — tests/test_musicgen/: 21 tests covering mock backend, factory,
and FastAPI app endpoints. musicgen module was already implemented;
tests were the missing piece to close the issue.
2026-04-20 11:10:49 -07:00
5149de0556 fix(text): fail fast on empty --model instead of crashing in backend
Add early validation in create_app(): raise ValueError with a clear
message when model_path is empty and mock=False. Prevents the cryptic
HFValidationError that surfaced when cf-orch passed an empty {model}
arg (cf-orch #46). Surfaces the real problem at the service layer
rather than deep inside the HuggingFace loader.
2026-04-20 10:50:49 -07:00
27f0b67908 feat(llm): pass CF_APP_NAME as pipeline tag in cf-orch allocations
Coordinator analytics now show which product (kiwi, peregrine, etc.) is
responsible for each vllm allocation, enabling per-app GPU usage breakdown.
2026-04-20 07:01:55 -07:00