Skip to content

Commit d39fe67

Browse files
committed
docs(ten-moss): add create-index example, docs, and repo integration entry
1 parent 6cbd4dc commit d39fe67

7 files changed

Lines changed: 210 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ packages/
6262
moss-md-indexer/ — Markdown docs → Moss index builder
6363
pipecat-moss/ — Pipecat Python integration package
6464
strands-agents-moss/ — AWS Strands Agents integration package
65+
ten-moss/ — TEN Framework ambient Moss retrieval helper (MossRetrievalStore)
6566
vapi-moss/ — VAPI Custom Knowledge Base webhook adapter
6667
vercel-sdk/ — Vercel AI SDK tool wrappers (@moss-tools/vercel-sdk)
6768
vitepress-plugin-moss/ — VitePress search plugin (on-device fallback after cloud)
@@ -137,6 +138,7 @@ asks for an experimental landing spot.
137138
| `pipecat-moss/` | `pipecat-moss` | `MossPipecatTool` — retrieval tool for Pipecat pipeline services |
138139
| `sim-moss/` | `sim-moss` | `MossSimSearch` — knowledge base adapter for sim.ai workflow HTTP tool nodes |
139140
| `strands-agents-moss/` | `strands-agents-moss` | Moss tool for AWS Strands Agents |
141+
| `ten-moss/` | `ten-moss` | `MossRetrievalStore` — ambient Moss retrieval for TEN Framework control extensions |
140142
| `vapi-moss/` | `vapi-moss` | `MossVapiSearch` adapter + HMAC webhook verification for VAPI |
141143
| `vercel-sdk/` | `@moss-tools/vercel-sdk` | Vercel AI SDK 6 `tool()` wrappers: search, create index, manage documents |
142144
| `vitepress-plugin-moss/` | `vitepress-plugin-moss` | VitePress plugin: cloud search on first keystroke, on-device after index download |

packages/ten-moss/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MOSS_PROJECT_ID=your_moss_project_id
2+
MOSS_PROJECT_KEY=your_moss_project_key
3+
MOSS_INDEX_NAME=ten-moss-demo

packages/ten-moss/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.0.1] - 2026-07-14
9+
10+
### Added
11+
- `MossRetrievalStore` — ambient Moss retrieval with `load`, `retrieve`, `format_context`, and `from_config`.
12+
- `MossRetrievalConfig` — standardized `moss_*` properties for TEN extensions.
13+
- `examples/create_index.py` — create and populate a demo index.

packages/ten-moss/CONTRIBUTING.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Contributing to ten-moss
2+
3+
## Setup
4+
5+
```bash
6+
cd packages/ten-moss
7+
uv sync
8+
```
9+
10+
## Test & lint
11+
12+
```bash
13+
uv run pytest tests/ -v
14+
uv run ruff check .
15+
uv run ruff format --check .
16+
```
17+
18+
Tests are offline (the Moss client is mocked) — no credentials required.

packages/ten-moss/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# ten-moss
2+
3+
Ambient sub-10ms semantic retrieval for the [TEN Framework](https://github.com/ten-framework/ten-framework), powered by [Moss](https://moss.dev).
4+
5+
`MossRetrievalStore` loads a Moss index once and returns a formatted context
6+
block for each user turn — drop it into a TEN control extension to ground your
7+
voice agent's answers. Retrieval failures degrade to an empty string, so the
8+
voice loop never stalls.
9+
10+
See `apps/ten-moss/` for a full runnable TEN voice-assistant example.
11+
12+
## Install
13+
14+
```bash
15+
pip install ten-moss # or: uv add ten-moss
16+
```
17+
18+
## Usage
19+
20+
```python
21+
from ten_moss import MossRetrievalStore
22+
23+
store = MossRetrievalStore(
24+
project_id="...", project_key="...", index_name="support-docs",
25+
top_k=5, alpha=0.8,
26+
)
27+
await store.load() # once, at startup
28+
context = await store.retrieve(user_text) # per turn; "" on no hits/error
29+
```
30+
31+
Or build it from TEN properties:
32+
33+
```python
34+
from ten_moss import MossRetrievalConfig, MossRetrievalStore
35+
36+
config = MossRetrievalConfig(**props) # moss_* fields from property.json
37+
store = MossRetrievalStore.from_config(config)
38+
```
39+
40+
## Configuration (`MossRetrievalConfig`)
41+
42+
| Field | Default | Meaning |
43+
| --- | --- | --- |
44+
| `moss_project_id` | `""` | Moss project id |
45+
| `moss_project_key` | `""` | Moss project key |
46+
| `moss_index_name` | `""` | index to load and query |
47+
| `moss_top_k` | `5` | results per query |
48+
| `moss_alpha` | `0.8` | semantic/keyword blend (1.0 semantic, 0.0 keyword) |
49+
| `moss_context_header` | `"Relevant knowledge from Moss:"` | header of the injected block |
50+
| `enable_moss` | `true` | master toggle |
51+
52+
## Create a demo index
53+
54+
```bash
55+
cp .env.example .env # fill in your Moss credentials
56+
python examples/create_index.py
57+
```
58+
59+
## Development
60+
61+
```bash
62+
uv sync
63+
uv run pytest tests/ -v
64+
uv run ruff check .
65+
```
66+
67+
Tests are offline (the Moss client is mocked) — no credentials required.
68+
69+
## License
70+
71+
BSD-2-Clause.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Create and populate a demo Moss index for the ten-moss integration.
2+
3+
Usage:
4+
export MOSS_PROJECT_ID=... MOSS_PROJECT_KEY=... MOSS_INDEX_NAME=...
5+
python examples/create_index.py
6+
"""
7+
8+
import asyncio
9+
import os
10+
11+
from dotenv import load_dotenv
12+
from loguru import logger
13+
from moss import DocumentInfo, MossClient
14+
15+
16+
def build_documents() -> list[DocumentInfo]:
17+
"""Return a small support knowledge base for the demo index."""
18+
return [
19+
DocumentInfo(
20+
id="doc-1",
21+
text="Refunds are processed within 3-5 business days once approved.",
22+
metadata={"category": "billing"},
23+
),
24+
DocumentInfo(
25+
id="doc-2",
26+
text="You can track your order from the dashboard under Order History.",
27+
metadata={"category": "orders"},
28+
),
29+
DocumentInfo(
30+
id="doc-3",
31+
text="We offer 24/7 live chat support from the Help menu.",
32+
metadata={"category": "support"},
33+
),
34+
DocumentInfo(
35+
id="doc-4",
36+
text="Standard shipping takes 3-5 business days; express takes 1-2.",
37+
metadata={"category": "shipping"},
38+
),
39+
DocumentInfo(
40+
id="doc-5",
41+
text="Reset your password using the Forgot Password link on the login page.",
42+
metadata={"category": "account"},
43+
),
44+
DocumentInfo(
45+
id="doc-6",
46+
text="We accept Visa, Mastercard, American Express, PayPal, and Apple Pay.",
47+
metadata={"category": "billing"},
48+
),
49+
DocumentInfo(
50+
id="doc-7",
51+
text="Orders can be cancelled within 1 hour of placement.",
52+
metadata={"category": "orders"},
53+
),
54+
DocumentInfo(
55+
id="doc-8",
56+
text="International shipping is available to most countries; rates vary.",
57+
metadata={"category": "shipping"},
58+
),
59+
DocumentInfo(
60+
id="doc-9",
61+
text="Gift wrapping is available at checkout for a small fee.",
62+
metadata={"category": "services"},
63+
),
64+
DocumentInfo(
65+
id="doc-10",
66+
text="We price-match authorized retailers within 14 days of purchase.",
67+
metadata={"category": "billing"},
68+
),
69+
]
70+
71+
72+
async def main() -> None:
73+
"""Create the index named by MOSS_INDEX_NAME from build_documents()."""
74+
load_dotenv()
75+
client = MossClient(os.environ["MOSS_PROJECT_ID"], os.environ["MOSS_PROJECT_KEY"])
76+
index_name = os.environ["MOSS_INDEX_NAME"]
77+
logger.info("Creating index {}", index_name)
78+
await client.create_index(name=index_name, docs=build_documents(), model_id="moss-minilm")
79+
logger.success("Index {} created", index_name)
80+
81+
82+
if __name__ == "__main__":
83+
asyncio.run(main())

packages/ten-moss/tests/test_retrieval_store.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Offline tests for the ten-moss helper package."""
22

33
import asyncio
4+
import importlib.util
5+
import pathlib
46
import unittest
57
from unittest.mock import AsyncMock, MagicMock, patch
68

@@ -129,5 +131,23 @@ async def test_from_config_builds_store(self, cls):
129131
self.assertEqual(store._context_header, "H")
130132

131133

134+
class TestCreateIndexExample(unittest.TestCase):
135+
"""The example script exposes a testable build_documents()."""
136+
137+
def _load_example(self):
138+
path = pathlib.Path(__file__).parent.parent / "examples" / "create_index.py"
139+
spec = importlib.util.spec_from_file_location("_ten_moss_create_index", path)
140+
module = importlib.util.module_from_spec(spec)
141+
spec.loader.exec_module(module)
142+
return module
143+
144+
def test_build_documents_returns_ten_docs(self):
145+
module = self._load_example()
146+
docs = module.build_documents()
147+
self.assertEqual(len(docs), 10)
148+
self.assertTrue(all(d.text for d in docs))
149+
self.assertEqual(len({d.id for d in docs}), 10) # unique ids
150+
151+
132152
if __name__ == "__main__":
133153
unittest.main()

0 commit comments

Comments
 (0)