Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,27 @@ jobs:
- name: Run tests
working-directory: packages/vitepress-plugin-moss
run: pnpm test

agora-ten-samples-smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
Comment thread
abojja9 marked this conversation as resolved.
with:
python-version: '3.11'
- name: Install smoke deps
run: |
python -m pip install --upgrade pip
pip install pytest pytest-asyncio fastapi httpx pydantic python-dotenv
- name: TEN graph + bench unit tests
run: |
pytest apps/ten-moss/tests apps/ten-moss/bench/test_run.py -v
- name: custom-llm mock/doctor tests
run: |
pytest apps/agora-custom-llm-moss/tests -v
- name: Echo-grounding table (no LLM keys)
run: |
python apps/ten-moss/bench/run.py --echo-grounding
12 changes: 11 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ examples/
moss-cognee-daytona/ β€” Claude Code + Cognee + Moss on Daytona (shared memory)
apps/
agora-moss/ β€” Agora Conversational AI voice agent (MCP server demo)
agora-custom-llm-moss/ β€” Agora custom-llm middleware: ambient prepend + in-process tool loop
docker/ β€” Dockerized Python + JS SDK examples (ECS/K8s pattern)
elevenlabs-moss/ β€” ElevenLabs voice agent with Moss knowledge base
livekit-moss-vercel/ β€” LiveKit voice agent + React frontend on Vercel
Expand Down Expand Up @@ -111,12 +112,13 @@ asks for an experimental landing spot.
| Directory | Integration | What it demonstrates |
| --------- | ----------- | -------------------- |
| `agora-moss/` | Agora Conversational AI | Moss as an MCP tool (`search_knowledge_base`) mounted on an Agora voice agent |
| `agora-custom-llm-moss/` | Agora custom-llm | OpenAI-compatible `/chat/completions` with Moss ambient + tool modes; Agora never sees the tool |
| `elevenlabs-moss/` | ElevenLabs | Knowledge-base-backed ElevenLabs Conversational AI bot with live Moss retrieval |
| `livekit-moss-vercel/` | LiveKit + Vercel | LiveKit voice agent with React frontend deployed to Vercel; Moss powers RAG |
| `pipecat-moss/pipecat-quickstart/` | Pipecat Cloud | Minimal Pipecat bot β€” local dev β†’ Pipecat Cloud deployment |
| `pipecat-moss/ollama-local/` | Pipecat + Ollama | Full-stack local voice AI: Ollama LLM + Moss RAG + Pipecat audio, one `docker compose up` |
| `pipecat-moss/hume-ollama-local/` | Pipecat + Ollama + Hume | Same as above with Hume AI (Octave) expressive TTS |
| `ten-moss/` | TEN Framework | Voice agent that grounds each turn in a Moss session (`MossSessionManager`); TEN `voice-assistant` example + the Moss delta |
| `ten-moss/` | TEN Framework | Voice agent that grounds each turn in a Moss session (`MossSessionManager`); graphs `voice_assistant` (ambient, default) and `voice_assistant_tools` (in-process `search_knowledge_base`) |
| `vapi-moss/` | VAPI | Webhook server connecting VAPI Custom Tool calls to Moss search; LLM-directed retrieval |

### Other Apps
Expand Down Expand Up @@ -273,4 +275,12 @@ The `.github/workflows/ci.yml` pipeline runs on push to `main` and on PRs:
- `python-lint` β€” ruff on examples and apps
- `python-sdk-test` β€” matrix over Python 3.10–3.14
- `javascript-lint` β€” eslint
- `agora-ten-samples-smoke` β€” TEN graph contract + `bench/run.py --echo-grounding` + custom-llm mock/doctor (no Agora/LLM keys)
- Separate release workflows publish to PyPI / npm on tagged releases

## Maintaining this file

Keep this file for knowledge useful to almost every future agent session in this project.
Do not repeat what the codebase already shows; point to the authoritative file or command instead.
Prefer rewriting or pruning existing entries over appending new ones.
When updating this file, preserve this bar for all agents and keep entries concise.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ apps/
β”œβ”€β”€ elevenlabs-moss/ # ElevenLabs voice agent with Moss retrieval
β”œβ”€β”€ livekit-moss-vercel/ # LiveKit voice agent on Vercel
β”œβ”€β”€ agora-moss/ # Agora Conversational AI MCP server with Moss retrieval
β”œβ”€β”€ agora-custom-llm-moss/ # Agora custom-llm middleware (ambient + in-process tool loop)
β”œβ”€β”€ ten-moss/ # TEN voice agent (ambient default + tool-call graph)
β”œβ”€β”€ moss-llamaindex/ # LlamaIndex RAG backend + frontend
β”œβ”€β”€ moss-bun/ # Bun runtime example
└── docker/ # Dockerized examples (ECS/K8s pattern)
Expand Down Expand Up @@ -238,8 +240,8 @@ Full API reference: [docs.moss.dev](https://docs.moss.dev).
| [LiveKit](https://github.com/livekit/livekit) | Available | [`apps/livekit-moss-vercel/`](apps/livekit-moss-vercel/) |
| [Vapi](https://vapi.ai) | Available | [`apps/vapi-moss/`](apps/vapi-moss/) |
| [ElevenLabs](https://elevenlabs.io) | Available | [`apps/elevenlabs-moss/`](apps/elevenlabs-moss/) |
| [Agora](https://www.agora.io/) | Available | [`apps/agora-moss/`](apps/agora-moss/) |
| [TEN Framework](https://github.com/ten-framework/ten-framework) | Available | [`apps/ten-moss/`](apps/ten-moss/) |
| [Agora](https://www.agora.io/) | Available | [`apps/agora-moss/`](apps/agora-moss/) (MCP) and [`apps/agora-custom-llm-moss/`](apps/agora-custom-llm-moss/) (custom-llm: ambient + tool-call) |
| [TEN Framework](https://github.com/ten-framework/ten-framework) | Available | [`apps/ten-moss/`](apps/ten-moss/) (ambient + tool-call) |
| [Strands Agents](https://github.com/strands-agents/sdk-python) | Available | [`packages/strands-agents-moss/`](packages/strands-agents-moss/) |
| [Langflow](https://github.com/langflow-ai/langflow) | Available | [`examples/cookbook/langflow/`](examples/cookbook/langflow/) |
| [Next.js](https://nextjs.org) | Available | [`apps/next-js/`](apps/next-js/) |
Expand Down
56 changes: 56 additions & 0 deletions apps/agora-custom-llm-moss/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Agora custom-llm + Moss

Agora cloud owns STT and TTS. This process owns `/chat/completions` and runs Moss next to the LLM.

This is not `apps/agora-moss`. That demo is MCP (Agora calls `search_knowledge_base`). Here Agora only sees an OpenAI-compatible URL.

Same 10 FAQs as `apps/ten-moss/data/knowledge.jsonl`.

## Copy these

| File | Why |
| --- | --- |
| `README.md` | this page |
| `server/src/llm.py` | one turn, both modes |
| `create_index.py` + `data/knowledge.jsonl` | same corpus as TEN |
| `server/.env.example` | `MOSS_*`, `CUSTOM_LLM_*`, `UPSTREAM_LLM_*` |

## One turn

**Ambient** (`/llm/chat/completions`, default): take the last user text, `query_context`, prepend, call the upstream model, stream SSE.

**Tool** (`/llm-tools/chat/completions`): give the upstream model `search_knowledge_base`. If it calls the tool, run Moss here (max 2 times). Stream only the final answer. Agora never sees the tool.

`MOSS_MODE=ambient|tool` picks the mode when you run `llm.py` alone.

## Run with no LLM keys

```bash
cd apps/agora-custom-llm-moss
python -m pip install -r server/requirements.txt
python server/src/llm.py --mock --doctor
```

Doctor hits both modes and checks that a missing `Authorization: Bearer` is rejected when mock is off.

```bash
python server/src/llm.py --mock --mode ambient # :8001/chat/completions
python server/src/server.py # /llm ambient, /llm-tools tool
```

## Run with a real index

```bash
cp server/.env.example server/.env # fill MOSS_* and a unique CUSTOM_LLM_API_KEY
python create_index.py
python server/src/server.py
ngrok http 8000
# CUSTOM_LLM_URL=https://<tunnel>/llm/chat/completions
# tool URL: https://<tunnel>/llm-tools/chat/completions
```

Set `CUSTOM_LLM_API_KEY` to a unique value before exposing the URL. If it is empty, every request is rejected, including any Bearer token. Send `Authorization: Bearer $CUSTOM_LLM_API_KEY`. If Moss is unset or errors, the handler returns empty context and still streams.

Offline table: `python apps/ten-moss/bench/run.py --echo-grounding`

`llm.py` keeps the Agora recipe SSE contract (MIT). The rest of this directory is BSD-2-Clause.
48 changes: 48 additions & 0 deletions apps/agora-custom-llm-moss/create_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Build the demo Moss index for the Agora custom-llm sample.

Same 10 FAQs as apps/ten-moss/data/knowledge.jsonl so the bench is shared.

Usage (from apps/agora-custom-llm-moss/):
cp server/.env.example server/.env
python create_index.py
"""

import asyncio
import json
import os
import pathlib

from dotenv import load_dotenv
from moss import DocumentInfo, MossClient

DATA = pathlib.Path(__file__).parent / "data" / "knowledge.jsonl"


def load_documents() -> list[DocumentInfo]:
"""Parse data/knowledge.jsonl into Moss documents."""
docs: list[DocumentInfo] = []
for line in DATA.read_text().splitlines():
line = line.strip()
if not line:
continue
row = json.loads(line)
docs.append(
DocumentInfo(id=row["id"], text=row["text"], metadata=row.get("metadata", {}))
)
return docs


async def main() -> None:
load_dotenv(pathlib.Path(__file__).parent / "server" / ".env")
load_dotenv()
client = MossClient(os.environ["MOSS_PROJECT_ID"], os.environ["MOSS_PROJECT_KEY"])
index_name = os.environ["MOSS_INDEX_NAME"]
docs = load_documents()
print(f"Creating index '{index_name}' with {len(docs)} documents...")
model_id = os.getenv("MOSS_MODEL_ID", "moss-minilm")
await client.create_index(name=index_name, docs=docs, model_id=model_id)
print("Done.")


if __name__ == "__main__":
asyncio.run(main())
10 changes: 10 additions & 0 deletions apps/agora-custom-llm-moss/data/knowledge.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{"id": "kb-1", "text": "Refunds are processed within 3-5 business days once the return is approved.", "metadata": {"category": "billing"}}
{"id": "kb-2", "text": "You can track an order from the dashboard under Order History using its tracking number.", "metadata": {"category": "orders"}}
{"id": "kb-3", "text": "Live chat support is available 24/7 from the Help menu in the app.", "metadata": {"category": "support"}}
{"id": "kb-4", "text": "Standard shipping takes 3-5 business days; express shipping takes 1-2 business days.", "metadata": {"category": "shipping"}}
{"id": "kb-5", "text": "Reset your password using the Forgot Password link on the login page.", "metadata": {"category": "account"}}
{"id": "kb-6", "text": "We accept Visa, Mastercard, American Express, PayPal, and Apple Pay.", "metadata": {"category": "billing"}}
{"id": "kb-7", "text": "Orders can be cancelled within 1 hour of placement, before they are dispatched.", "metadata": {"category": "orders"}}
{"id": "kb-8", "text": "International shipping is available to most countries; rates and delivery times vary by destination.", "metadata": {"category": "shipping"}}
{"id": "kb-9", "text": "Gift wrapping with a personalized message is available at checkout for a small fee.", "metadata": {"category": "services"}}
{"id": "kb-10", "text": "We price-match identical items from authorized retailers within 14 days of purchase.", "metadata": {"category": "billing"}}
2 changes: 2 additions & 0 deletions apps/agora-custom-llm-moss/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
testpaths = tests
24 changes: 24 additions & 0 deletions apps/agora-custom-llm-moss/server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# --- Moss (same index / corpus as apps/ten-moss) ---
MOSS_PROJECT_ID=
MOSS_PROJECT_KEY=
MOSS_INDEX_NAME=ten-moss-demo
MOSS_MODEL_ID=moss-minilm

# ambient (default) or tool. server.py mounts both regardless:
# /llm = ambient
# /llm-tools = tool
MOSS_MODE=ambient

# --- This endpoint (what Agora cloud calls) ---
CUSTOM_LLM_API_KEY=
CUSTOM_LLM_MODEL=moss-custom-llm
CUSTOM_LLM_PORT=8001
PORT=8000

# --- Upstream chat model (not needed for --mock / --doctor) ---
UPSTREAM_LLM_URL=https://api.openai.com/v1
UPSTREAM_LLM_API_KEY=
UPSTREAM_LLM_MODEL=gpt-4o-mini

# Set MOCK=1 or pass --mock for the zero-key path.
# MOCK=0
5 changes: 5 additions & 0 deletions apps/agora-custom-llm-moss/server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
.env.local
__pycache__/
.venv/
venv/
6 changes: 6 additions & 0 deletions apps/agora-custom-llm-moss/server/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fastapi>=0.100.0
uvicorn>=0.20.0
python-dotenv>=1.0.0
httpx>=0.27.0
ten-moss>=0.1.0
pydantic>=2.0.0
Loading
Loading