anchorcast 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. anchorcast-0.1.0/.github/workflows/ci.yml +28 -0
  2. anchorcast-0.1.0/.github/workflows/publish.yml +31 -0
  3. anchorcast-0.1.0/.gitignore +11 -0
  4. anchorcast-0.1.0/AGENTS.md +44 -0
  5. anchorcast-0.1.0/CHANGELOG.md +23 -0
  6. anchorcast-0.1.0/CODE_OF_CONDUCT.md +62 -0
  7. anchorcast-0.1.0/CONTRIBUTING.md +43 -0
  8. anchorcast-0.1.0/LICENSE +21 -0
  9. anchorcast-0.1.0/PKG-INFO +177 -0
  10. anchorcast-0.1.0/README.md +150 -0
  11. anchorcast-0.1.0/SECURITY.md +17 -0
  12. anchorcast-0.1.0/examples/__init__.py +1 -0
  13. anchorcast-0.1.0/examples/characters/literary-bear/CREDIT.txt +3 -0
  14. anchorcast-0.1.0/examples/characters/literary-bear/pooh-1926.png +0 -0
  15. anchorcast-0.1.0/examples/feed_session.py +38 -0
  16. anchorcast-0.1.0/examples/literary_bear/__init__.py +1 -0
  17. anchorcast-0.1.0/examples/literary_bear/__main__.py +6 -0
  18. anchorcast-0.1.0/examples/literary_bear/app.py +209 -0
  19. anchorcast-0.1.0/examples/literary_bear/player.html +192 -0
  20. anchorcast-0.1.0/examples/literary_bear/scripts.py +119 -0
  21. anchorcast-0.1.0/examples/literary_bear/source.py +44 -0
  22. anchorcast-0.1.0/pyproject.toml +40 -0
  23. anchorcast-0.1.0/src/anchorcast/__init__.py +32 -0
  24. anchorcast-0.1.0/src/anchorcast/continuity.py +19 -0
  25. anchorcast-0.1.0/src/anchorcast/media.py +48 -0
  26. anchorcast-0.1.0/src/anchorcast/models/__init__.py +5 -0
  27. anchorcast-0.1.0/src/anchorcast/models/base.py +5 -0
  28. anchorcast-0.1.0/src/anchorcast/models/h3_max.py +61 -0
  29. anchorcast-0.1.0/src/anchorcast/py.typed +0 -0
  30. anchorcast-0.1.0/src/anchorcast/session.py +98 -0
  31. anchorcast-0.1.0/src/anchorcast/sources/__init__.py +7 -0
  32. anchorcast-0.1.0/src/anchorcast/sources/feed.py +39 -0
  33. anchorcast-0.1.0/src/anchorcast/sources/idle.py +18 -0
  34. anchorcast-0.1.0/src/anchorcast/sources/queue.py +21 -0
  35. anchorcast-0.1.0/src/anchorcast/types.py +84 -0
  36. anchorcast-0.1.0/tests/test_character.py +23 -0
  37. anchorcast-0.1.0/tests/test_continuation_prompt.py +39 -0
  38. anchorcast-0.1.0/tests/test_feed_source.py +29 -0
  39. anchorcast-0.1.0/tests/test_idle_source.py +19 -0
  40. anchorcast-0.1.0/tests/test_literary_scripts.py +21 -0
  41. anchorcast-0.1.0/tests/test_queue_source.py +18 -0
  42. anchorcast-0.1.0/tests/test_session.py +76 -0
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.11", "3.12", "3.13"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ - name: Install
24
+ run: python -m pip install -e ".[dev]"
25
+ - name: Ruff
26
+ run: python -m ruff check src tests
27
+ - name: Pytest
28
+ run: python -m pytest
@@ -0,0 +1,31 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write
16
+ contents: read
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.11"
22
+ - name: Tag matches package version
23
+ run: |
24
+ tag="${GITHUB_REF_NAME#v}"
25
+ version="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")"
26
+ test "$tag" = "$version"
27
+ - name: Build
28
+ run: |
29
+ python -m pip install build
30
+ python -m build
31
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .anchorcast/
8
+ .pytest_cache/
9
+ /segments/
10
+ .env*
11
+ .DS_Store
@@ -0,0 +1,44 @@
1
+ # Agent notes
2
+
3
+ This repository is a **Python client** for generative livestreams. It is not a YouTube app, not a character, and not a news show.
4
+
5
+ ## Layout
6
+
7
+ - `src/anchorcast/` — library
8
+ - `tests/` — pytest, no fal network calls
9
+ - `examples/` — apps that consume the library, including a literary-bear news stream
10
+
11
+ ## Architecture
12
+
13
+ Keep these boundaries:
14
+
15
+ | Layer | Owns |
16
+ | --- | --- |
17
+ | `Brief` | What to say next (topic, talking points, citations) |
18
+ | `Model` | `generate(request) -> Segment` |
19
+ | `Source` | `next_brief(playhead) -> Brief \| None` |
20
+ | `Continuity` | Which still starts the next clip |
21
+ | `Session` | Generate-ahead buffer and events |
22
+
23
+ Do **not** add YouTube, Twitch, TikTok, Stripe, or Super Chat integrations here. Those belong in a later backend that maps platform events into `Brief`s on a `QueueSource`.
24
+
25
+ Do **not** let `IdleSource` (or any default source) invent news claims. Idle is character bits only.
26
+
27
+ ## Python
28
+
29
+ - 3.11+
30
+ - Double quotes
31
+ - Type annotations on public functions and dataclasses
32
+ - `gpt-5.6-luna` for OpenAI calls in examples (cheap GPT-5.6 tier)
33
+
34
+ ## Checks
35
+
36
+ ```bash
37
+ python3.11 -m pip install -e ".[dev]"
38
+ python3.11 -m pytest
39
+ python3.11 -m ruff check src tests
40
+ ```
41
+
42
+ New behavior needs a failing test first unless it is config, examples, or generated files.
43
+
44
+ The H3 Max adapter hits fal. Do not call it from unit tests. Use a fake `Model`.
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-09-03
9
+
10
+ ### Added
11
+
12
+ - `Session` with a generate-ahead buffer
13
+ - `Brief`, `Character`, `Segment`, and `StreamEvent` types
14
+ - `H3Max` model adapter (`minimax/h3-max/image-to-video`)
15
+ - `QueueSource`, `FeedSource`, and `IdleSource`
16
+ - `LastFrameContinuity` and `IdentityContinuity`
17
+ - MIT license
18
+ - GitHub Actions CI (pytest, ruff) and tag-triggered PyPI publish
19
+ - `Session.pump()` for live apps
20
+ - Follow-up clips prompt the model to continue the take
21
+ - `examples/literary_bear` continuous news stream with continuing spoken lines
22
+
23
+ `Brief` has no paid/unpaid taxonomy. Use `priority` to order the queue.
@@ -0,0 +1,62 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment:
18
+
19
+ - Demonstrating empathy and kindness toward other people
20
+ - Being respectful of differing opinions, viewpoints, and experiences
21
+ - Giving and gracefully accepting constructive feedback
22
+ - Accepting responsibility and apologizing to those affected by our mistakes,
23
+ and learning from the experience
24
+ - Focusing on what is best not just for us as individuals, but for the overall
25
+ community
26
+
27
+ Examples of unacceptable behavior:
28
+
29
+ - The use of sexualized language or imagery, and sexual attention or advances of
30
+ any kind
31
+ - Trolling, insulting or derogatory comments, and personal or political attacks
32
+ - Public or private harassment
33
+ - Publishing others' private information, such as a physical or email address,
34
+ without their explicit permission
35
+ - Other conduct which could reasonably be considered inappropriate in a
36
+ professional setting
37
+
38
+ ## Enforcement Responsibilities
39
+
40
+ Project maintainers are responsible for clarifying and enforcing our standards of
41
+ acceptable behavior and will take appropriate and fair corrective action in
42
+ response to any behavior that they deem inappropriate, threatening, offensive,
43
+ or harmful.
44
+
45
+ ## Scope
46
+
47
+ This Code of Conduct applies within all community spaces, and also applies when
48
+ an individual is officially representing the community in public spaces.
49
+
50
+ ## Enforcement
51
+
52
+ Report incidents by opening a private GitHub security advisory on this
53
+ repository or by contacting the maintainer through GitHub. All complaints will
54
+ be reviewed and investigated promptly and fairly.
55
+
56
+ ## Attribution
57
+
58
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
59
+ version 2.1, available at
60
+ https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
61
+
62
+ [homepage]: https://www.contributor-covenant.org
@@ -0,0 +1,43 @@
1
+ # Contributing
2
+
3
+ Thanks for looking at Anchorcast. The useful work is the `Session` / `Model` / `Source` / `Brief` interface. Keep that small.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ python3.11 -m pip install -e ".[dev]"
9
+ python3.11 -m pytest
10
+ python3.11 -m ruff check src tests
11
+ ```
12
+
13
+ Python 3.11+ and ffmpeg on `PATH`. Unit tests must not call fal.
14
+
15
+ ## Pull requests
16
+
17
+ - One idea per PR
18
+ - Tests for behavior changes
19
+ - Do not land YouTube / Twitch / TikTok / payments in this repo
20
+ - Match existing style: double quotes, type hints
21
+
22
+ Coding agents should read [AGENTS.md](AGENTS.md).
23
+
24
+ ## Releasing
25
+
26
+ CI runs pytest and ruff on every push and pull request.
27
+
28
+ PyPI publishes from a version tag via [trusted publishing](https://docs.pypi.org/trusted-publishers/). No API token in GitHub secrets.
29
+
30
+ 1. Bump `version` in `pyproject.toml` and `src/anchorcast/__init__.py`, and add a `CHANGELOG.md` entry.
31
+ 2. On PyPI: Publishing → add a trusted publisher (`scrollmark/anchorcast`, workflow `publish.yml`, environment `pypi`). Needed once, before the first release.
32
+ 3. Tag and push:
33
+
34
+ ```bash
35
+ git tag v0.1.0
36
+ git push origin v0.1.0
37
+ ```
38
+
39
+ The tag must match the package version (`v0.1.0` for `0.1.0`). The `pypi` GitHub Environment must exist on the repo.
40
+
41
+ ## License
42
+
43
+ Contributions are MIT, same as the rest of the project.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nikhil Sharma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.5
2
+ Name: anchorcast
3
+ Version: 0.1.0
4
+ Summary: A Python client for real-time media streams generated by pluggable models.
5
+ Project-URL: Homepage, https://github.com/scrollmark/anchorcast
6
+ Project-URL: Repository, https://github.com/scrollmark/anchorcast
7
+ Project-URL: Issues, https://github.com/scrollmark/anchorcast/issues
8
+ Author: Nikhil Sharma
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: fal,generative-video,h3-max,livestream
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Multimedia :: Video
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: fal-client>=0.5.0
22
+ Requires-Dist: feedparser>=6.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == 'dev'
25
+ Requires-Dist: ruff>=0.9; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Anchorcast
29
+
30
+ A Python client for **real-time media streams generated by pluggable models**.
31
+
32
+ [![CI](https://github.com/scrollmark/anchorcast/actions/workflows/ci.yml/badge.svg)](https://github.com/scrollmark/anchorcast/actions/workflows/ci.yml)
33
+ [![PyPI](https://img.shields.io/pypi/v/anchorcast)](https://pypi.org/project/anchorcast/)
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
35
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
36
+
37
+ > Early (`0.1.0`). The session / model / source interface is the point. Expect it to move.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install anchorcast
43
+ ```
44
+
45
+ From git:
46
+
47
+ ```bash
48
+ pip install git+https://github.com/scrollmark/anchorcast.git
49
+ ```
50
+
51
+ For local development:
52
+
53
+ ```bash
54
+ git clone https://github.com/scrollmark/anchorcast.git
55
+ cd anchorcast
56
+ python3.11 -m pip install -e ".[dev]"
57
+ python3.11 -m pytest
58
+ ```
59
+
60
+ **Requirements:** Python 3.11+, [ffmpeg](https://ffmpeg.org/) on `PATH` (for last-frame continuity), and a [fal](https://fal.ai) key if you use the H3 Max adapter (`FAL_KEY` or `FALAI_API_KEY`).
61
+
62
+ ## Quickstart
63
+
64
+ ```python
65
+ from pathlib import Path
66
+
67
+ from anchorcast import Brief, Character, QueueSource, Session
68
+ from anchorcast.models import H3Max
69
+
70
+ source = QueueSource()
71
+ session = Session(
72
+ model=H3Max(),
73
+ character=Character(
74
+ name="Bear",
75
+ image=Path("bear.png"),
76
+ visual_prompt="Hand-drawn stuffed bear on cream paper.",
77
+ voice_prompt="warm, unhurried cadence",
78
+ ),
79
+ source=source,
80
+ workdir=Path(".anchorcast"),
81
+ )
82
+
83
+ source.submit(
84
+ Brief(
85
+ id="rates",
86
+ topic="mortgage rates",
87
+ talking_points=("Why are mortgage rates still so high?",),
88
+ citations=("https://example.com/rates",),
89
+ priority=10,
90
+ )
91
+ )
92
+
93
+ for event in session.run(max_segments=1):
94
+ print(event.segment.path, event.segment.brief.topic)
95
+ ```
96
+
97
+ A `Brief` is the next beat of the stream. The session asks the source for one, asks the model for a clip, and keeps a small generate-ahead buffer so you do not mint faster than the playhead.
98
+
99
+ ## Core types
100
+
101
+ | Type | Role |
102
+ | --- | --- |
103
+ | **`Brief`** | Topic, talking points, citations, priority |
104
+ | **`Character`** | Seed still, visual prompt, voice description |
105
+ | **`Model`** | `generate(request) -> Segment`. `H3Max` ships first; swap the adapter to change models |
106
+ | **`Source`** | `next_brief(playhead) -> Brief \| None` |
107
+ | **`Continuity`** | Which still starts the next clip (`LastFrameContinuity` or `IdentityContinuity`) |
108
+ | **`Session`** | Buffer, pacing, event stream |
109
+
110
+ ### Sources
111
+
112
+ - **`QueueSource`** — explicit briefs, ordered by `priority`
113
+ - **`FeedSource`** — RSS/Atom mapped to cited stories
114
+ - **`IdleSource`** — character bits that make **no news claims**
115
+
116
+ When the queue is empty, pass `idle=IdleSource(character)` so the session can keep breathing without inventing facts.
117
+
118
+ ### Custom model
119
+
120
+ ```python
121
+ from anchorcast.types import GenerateRequest, Segment
122
+
123
+ class MyModel:
124
+ def generate(self, request: GenerateRequest) -> Segment:
125
+ # write a video to request.output_path
126
+ return Segment(
127
+ path=request.output_path,
128
+ duration=15.0,
129
+ generated_in=1.0,
130
+ brief=request.brief,
131
+ )
132
+ ```
133
+
134
+ ### Custom source
135
+
136
+ ```python
137
+ from anchorcast.types import Brief, Playhead
138
+
139
+ class WebhookSource:
140
+ def next_brief(self, playhead: Playhead | None = None) -> Brief | None:
141
+ return self._take_from_queue()
142
+ ```
143
+
144
+ YouTube Live, Twitch, TikTok Live, and Super Chats are a later backend. That layer should map platform events into `Brief`s and submit them to a `QueueSource`. It does not belong in this client.
145
+
146
+ ## Status
147
+
148
+ - Generate-ahead `Session` with a pluggable model and source
149
+ - fal MiniMax H3 Max adapter (`minimax/h3-max/image-to-video`)
150
+ - Feed, queue, and idle sources
151
+ - Last-frame continuity via ffmpeg
152
+
153
+ Not yet: RTMP output, hosted APIs, or platform chat integrations.
154
+
155
+ ## Example app
156
+
157
+ `examples/literary_bear` is a continuous 1926 literary-bear news stream (Shepard, not Disney). Each clip is the next fifteen seconds of one monologue, not a fresh "oh bother" cold open.
158
+
159
+ ```bash
160
+ python3.11 -m examples.literary_bear --dry-run # scripts only
161
+ python3.11 -m examples.literary_bear # local player at http://127.0.0.1:8765
162
+ ```
163
+
164
+ `--llm` or `OPENAI_API_KEY` writes spoken lines with `gpt-5.6-luna`. `--no-llm` forces the rotating templates.
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ python3.11 -m pip install -e ".[dev]"
170
+ python3.11 -m pytest
171
+ ```
172
+
173
+ ## License
174
+
175
+ MIT. See [LICENSE](LICENSE).
176
+
177
+ The example still under `examples/characters/literary-bear/` is an E. H. Shepard illustration from *Winnie-the-Pooh* (1926), U.S. public domain. It is not Disney, and this project is not affiliated with Disney.
@@ -0,0 +1,150 @@
1
+ # Anchorcast
2
+
3
+ A Python client for **real-time media streams generated by pluggable models**.
4
+
5
+ [![CI](https://github.com/scrollmark/anchorcast/actions/workflows/ci.yml/badge.svg)](https://github.com/scrollmark/anchorcast/actions/workflows/ci.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/anchorcast)](https://pypi.org/project/anchorcast/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
9
+
10
+ > Early (`0.1.0`). The session / model / source interface is the point. Expect it to move.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install anchorcast
16
+ ```
17
+
18
+ From git:
19
+
20
+ ```bash
21
+ pip install git+https://github.com/scrollmark/anchorcast.git
22
+ ```
23
+
24
+ For local development:
25
+
26
+ ```bash
27
+ git clone https://github.com/scrollmark/anchorcast.git
28
+ cd anchorcast
29
+ python3.11 -m pip install -e ".[dev]"
30
+ python3.11 -m pytest
31
+ ```
32
+
33
+ **Requirements:** Python 3.11+, [ffmpeg](https://ffmpeg.org/) on `PATH` (for last-frame continuity), and a [fal](https://fal.ai) key if you use the H3 Max adapter (`FAL_KEY` or `FALAI_API_KEY`).
34
+
35
+ ## Quickstart
36
+
37
+ ```python
38
+ from pathlib import Path
39
+
40
+ from anchorcast import Brief, Character, QueueSource, Session
41
+ from anchorcast.models import H3Max
42
+
43
+ source = QueueSource()
44
+ session = Session(
45
+ model=H3Max(),
46
+ character=Character(
47
+ name="Bear",
48
+ image=Path("bear.png"),
49
+ visual_prompt="Hand-drawn stuffed bear on cream paper.",
50
+ voice_prompt="warm, unhurried cadence",
51
+ ),
52
+ source=source,
53
+ workdir=Path(".anchorcast"),
54
+ )
55
+
56
+ source.submit(
57
+ Brief(
58
+ id="rates",
59
+ topic="mortgage rates",
60
+ talking_points=("Why are mortgage rates still so high?",),
61
+ citations=("https://example.com/rates",),
62
+ priority=10,
63
+ )
64
+ )
65
+
66
+ for event in session.run(max_segments=1):
67
+ print(event.segment.path, event.segment.brief.topic)
68
+ ```
69
+
70
+ A `Brief` is the next beat of the stream. The session asks the source for one, asks the model for a clip, and keeps a small generate-ahead buffer so you do not mint faster than the playhead.
71
+
72
+ ## Core types
73
+
74
+ | Type | Role |
75
+ | --- | --- |
76
+ | **`Brief`** | Topic, talking points, citations, priority |
77
+ | **`Character`** | Seed still, visual prompt, voice description |
78
+ | **`Model`** | `generate(request) -> Segment`. `H3Max` ships first; swap the adapter to change models |
79
+ | **`Source`** | `next_brief(playhead) -> Brief \| None` |
80
+ | **`Continuity`** | Which still starts the next clip (`LastFrameContinuity` or `IdentityContinuity`) |
81
+ | **`Session`** | Buffer, pacing, event stream |
82
+
83
+ ### Sources
84
+
85
+ - **`QueueSource`** — explicit briefs, ordered by `priority`
86
+ - **`FeedSource`** — RSS/Atom mapped to cited stories
87
+ - **`IdleSource`** — character bits that make **no news claims**
88
+
89
+ When the queue is empty, pass `idle=IdleSource(character)` so the session can keep breathing without inventing facts.
90
+
91
+ ### Custom model
92
+
93
+ ```python
94
+ from anchorcast.types import GenerateRequest, Segment
95
+
96
+ class MyModel:
97
+ def generate(self, request: GenerateRequest) -> Segment:
98
+ # write a video to request.output_path
99
+ return Segment(
100
+ path=request.output_path,
101
+ duration=15.0,
102
+ generated_in=1.0,
103
+ brief=request.brief,
104
+ )
105
+ ```
106
+
107
+ ### Custom source
108
+
109
+ ```python
110
+ from anchorcast.types import Brief, Playhead
111
+
112
+ class WebhookSource:
113
+ def next_brief(self, playhead: Playhead | None = None) -> Brief | None:
114
+ return self._take_from_queue()
115
+ ```
116
+
117
+ YouTube Live, Twitch, TikTok Live, and Super Chats are a later backend. That layer should map platform events into `Brief`s and submit them to a `QueueSource`. It does not belong in this client.
118
+
119
+ ## Status
120
+
121
+ - Generate-ahead `Session` with a pluggable model and source
122
+ - fal MiniMax H3 Max adapter (`minimax/h3-max/image-to-video`)
123
+ - Feed, queue, and idle sources
124
+ - Last-frame continuity via ffmpeg
125
+
126
+ Not yet: RTMP output, hosted APIs, or platform chat integrations.
127
+
128
+ ## Example app
129
+
130
+ `examples/literary_bear` is a continuous 1926 literary-bear news stream (Shepard, not Disney). Each clip is the next fifteen seconds of one monologue, not a fresh "oh bother" cold open.
131
+
132
+ ```bash
133
+ python3.11 -m examples.literary_bear --dry-run # scripts only
134
+ python3.11 -m examples.literary_bear # local player at http://127.0.0.1:8765
135
+ ```
136
+
137
+ `--llm` or `OPENAI_API_KEY` writes spoken lines with `gpt-5.6-luna`. `--no-llm` forces the rotating templates.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ python3.11 -m pip install -e ".[dev]"
143
+ python3.11 -m pytest
144
+ ```
145
+
146
+ ## License
147
+
148
+ MIT. See [LICENSE](LICENSE).
149
+
150
+ The example still under `examples/characters/literary-bear/` is an E. H. Shepard illustration from *Winnie-the-Pooh* (1926), U.S. public domain. It is not Disney, and this project is not affiliated with Disney.
@@ -0,0 +1,17 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ `0.1.x` on `main` is the only supported line.
6
+
7
+ ## Reporting
8
+
9
+ Please **do not** open a public issue for a vulnerability.
10
+
11
+ Use GitHub's private reporting:
12
+
13
+ https://github.com/scrollmark/anchorcast/security/advisories/new
14
+
15
+ Include what you ran, what you expected, and a minimal reproduction if you have one.
16
+
17
+ This client talks to third-party generation APIs. Never commit API keys. `FAL_KEY` / `FALAI_API_KEY` stay in the environment.
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -0,0 +1,3 @@
1
+ E. H. Shepard illustration from Winnie-the-Pooh (1926), U.S. public domain.
2
+ Source: Wikimedia Commons, File:Winnie-the-Pooh_42.png
3
+ Not Disney. Shepard's UK copyright may still apply in the UK until 2046.
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from anchorcast import Character, FeedSource, IdleSource, Session
6
+ from anchorcast.continuity import LastFrameContinuity
7
+ from anchorcast.models import H3Max
8
+
9
+ ROOT = Path(__file__).resolve().parent.parent
10
+
11
+
12
+ def main() -> None:
13
+ character = Character(
14
+ name="Bear",
15
+ image=ROOT / "examples" / "characters" / "literary-bear" / "pooh-1926.png",
16
+ visual_prompt=(
17
+ "Hand-drawn 1926 book illustration of a stuffed bear of very little brain. "
18
+ "Pencil and light watercolor on cream paper. No clothing."
19
+ ),
20
+ voice_prompt="warm, soft, slightly posh, unhurried stuffed-bear cadence",
21
+ )
22
+ source = FeedSource("https://feeds.bbci.co.uk/news/world/rss.xml")
23
+ session = Session(
24
+ model=H3Max(),
25
+ character=character,
26
+ source=source,
27
+ idle=IdleSource(character),
28
+ workdir=ROOT / ".anchorcast",
29
+ continuity=LastFrameContinuity(),
30
+ buffer_clips=2,
31
+ )
32
+ for event in session.run(max_segments=2):
33
+ clip = event.segment
34
+ print(f"{event.index:02d} {clip.duration:.1f}s in {clip.generated_in:.1f}s {clip.brief.topic}")
35
+
36
+
37
+ if __name__ == "__main__":
38
+ main()
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from examples.literary_bear.app import main
4
+
5
+ if __name__ == "__main__":
6
+ main()