Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce4c302d73 | ||
|
|
685ca994f9 | ||
|
|
285b0699ab | ||
|
|
7825489cfc | ||
|
|
fb2fa31f13 | ||
|
|
9eb4c48d79 | ||
|
|
e4d465ba42 | ||
|
|
61d01984ba | ||
|
|
113553c306 | ||
|
|
736e6702f5 | ||
|
|
80256012ad | ||
|
|
bb1cf6941b | ||
|
|
2c8b4b8186 | ||
|
|
95a17197ba | ||
|
|
d09f9f8ac4 | ||
|
|
de16feeed5 | ||
|
|
cdcc804ffa | ||
|
|
9a2bbd78a4 | ||
|
|
ddfbd6891b | ||
|
|
143dd47e02 | ||
|
|
cc7cb773d1 | ||
|
|
02c88f75a1 | ||
|
|
419371e0fb | ||
|
|
6e847bf283 | ||
|
|
ceedd3019f | ||
|
|
a28cf2a029 | ||
|
|
60cb271e30 | ||
|
|
1874234d2c | ||
|
|
ef99539e69 | ||
|
|
39bd02722d | ||
|
|
f0b627b900 | ||
|
|
95ae00e01f | ||
|
|
83027b3a0f | ||
|
|
27eba3bd63 | ||
|
|
ba64050eab | ||
|
|
199ffd1095 | ||
|
|
88b9fcb8bb | ||
|
|
2f5944bc09 | ||
|
|
abdfb1abbf | ||
|
|
2f07248211 | ||
|
|
9ae6fc822b | ||
|
|
5094359c4e | ||
|
|
28801b54bf | ||
|
|
4d69f0e837 | ||
|
|
d91b3f8042 | ||
|
|
03a0ab2fcf | ||
|
|
d860d62972 | ||
|
|
add4893939 | ||
|
|
eb251d6e37 | ||
|
|
4ba635497b | ||
|
|
bdbf14c1ed | ||
|
|
c340fc085c | ||
|
|
94f8d0f242 | ||
|
|
1d8a9b113c | ||
|
|
1837796852 | ||
|
|
c5c53c056e | ||
|
|
f1b1190f0b | ||
|
|
303ec2a139 | ||
|
|
0303a42699 | ||
|
|
d768713630 | ||
|
|
33b043b56d | ||
|
|
b6f4069e8c | ||
|
|
1cb5869f0b | ||
|
|
8ad794aa6c | ||
|
|
611f77a730 | ||
|
|
0738899ac9 | ||
|
|
76b7b5c0e0 | ||
|
|
cae5e8d217 | ||
|
|
97bfd3a672 | ||
|
|
8fb1341b93 | ||
|
|
cba4412f3d | ||
|
|
2ad87f6e8a | ||
|
|
496eb469db | ||
|
|
b78fec3bac |
57
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
container-build:
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build & push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
|
||||
|
||||
deploy-docs:
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure Git credentials
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: '3.x' }
|
||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
key: mkdocs-material-${{ env.cache_id }}
|
||||
path: .cache
|
||||
restore-keys: mkdocs-material-
|
||||
- run: pip install mkdocs-material mkdocs-awesome-pages-plugin mkdocs-glightbox
|
||||
- run: mkdocs gh-deploy --force
|
||||
32
.github/workflows/test-container-build.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: test-container-build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'prep-*' ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
container-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build & push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
# Tag with prep suffix to avoid conflicts with production
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
|
||||
62
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Python Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, 'prep-*' ]
|
||||
pull_request:
|
||||
branches: [ main, 'prep-*' ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install uv
|
||||
|
||||
- name: Cache uv dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/uv
|
||||
key: ${{ runner.os }}-uv-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-uv-${{ matrix.python-version }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv venv
|
||||
source .venv/bin/activate
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run linting
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
uv run pre-commit run --all-files
|
||||
|
||||
- name: Setup configuration file
|
||||
run: |
|
||||
cp config.example.yaml config.yaml
|
||||
|
||||
- name: Download NLTK data
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
python -c "import nltk; nltk.download('punkt_tab')"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
pytest tests/ -p no:warnings
|
||||
25
.gitignore
vendored
@@ -7,7 +7,28 @@
|
||||
*_internal*
|
||||
talemate_env
|
||||
chroma
|
||||
scenes
|
||||
config.yaml
|
||||
!scenes/infinity-quest/assets
|
||||
.cursor
|
||||
.claude
|
||||
|
||||
# uv
|
||||
.venv/
|
||||
templates/llm-prompt/user/*.jinja2
|
||||
templates/world-state/*.yaml
|
||||
tts/voice/piper/*.onnx
|
||||
tts/voice/piper/*.json
|
||||
tts/voice/kokoro/*.pt
|
||||
tts/voice/xtts2/*.wav
|
||||
tts/voice/chatterbox/*.wav
|
||||
tts/voice/f5tts/*.wav
|
||||
tts/voice/voice-library.json
|
||||
scenes/
|
||||
!scenes/infinity-quest-dynamic-scenario/
|
||||
!scenes/infinity-quest-dynamic-scenario/assets/
|
||||
!scenes/infinity-quest-dynamic-scenario/templates/
|
||||
!scenes/infinity-quest-dynamic-scenario/infinity-quest.json
|
||||
!scenes/infinity-quest/assets/
|
||||
!scenes/infinity-quest/infinity-quest.json
|
||||
tts_voice_samples/*.wav
|
||||
third-party-docs/
|
||||
legacy-state-reinforcements.yaml
|
||||
16
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
fail_fast: false
|
||||
exclude: |
|
||||
(?x)^(
|
||||
tests/data/.*
|
||||
|install-utils/.*
|
||||
)$
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.12.1
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
args: [ --fix ]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.11
|
||||
64
CONTRIBUTING.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Contributing to Talemate
|
||||
|
||||
## About This Project
|
||||
|
||||
Talemate is a **personal hobbyist project** that I maintain in my spare time. While I appreciate the community's interest and contributions, please understand that:
|
||||
|
||||
- This is primarily a passion project that I enjoy working on myself
|
||||
- I have limited time for code reviews and prefer to spend that time developing fixes or new features myself
|
||||
- Large contributions require significant review and testing time that takes away from my own development
|
||||
|
||||
For these reasons, I've established contribution guidelines that balance community involvement with my desire to actively develop the project myself.
|
||||
|
||||
## Contribution Policy
|
||||
|
||||
**I welcome small bugfix and small feature pull requests!** If you've found a bug and have a fix, or have a small feature improvement, I'd love to review it.
|
||||
|
||||
However, please note that **I am not accepting large refactors or major feature additions** at this time. This includes:
|
||||
- Major architectural changes
|
||||
- Large new features or significant functionality additions
|
||||
- Large-scale code reorganization
|
||||
- Breaking API changes
|
||||
- Features that would require significant maintenance
|
||||
|
||||
## What is accepted
|
||||
|
||||
✅ **Small bugfixes** - Fixes for specific, isolated bugs
|
||||
|
||||
✅ **Small features** - Minor improvements that don't break existing functionality
|
||||
|
||||
✅ **Documentation fixes** - Typo corrections, clarifications in existing docs
|
||||
|
||||
✅ **Minor dependency updates** - Security patches or minor version bumps
|
||||
|
||||
## What is not accepted
|
||||
|
||||
❌ **Major features** - Large new functionality or systems
|
||||
|
||||
❌ **Large refactors** - Code reorganization or architectural changes
|
||||
|
||||
❌ **Breaking changes** - Any changes that break existing functionality
|
||||
|
||||
❌ **Major dependency changes** - Framework upgrades or replacements
|
||||
|
||||
## Submitting a PR
|
||||
|
||||
If you'd like to submit a bugfix or small feature:
|
||||
|
||||
1. **Open an issue first** - Describe the bug you've found or feature you'd like to add
|
||||
2. **Keep it small** - Focus on one specific issue or small improvement
|
||||
3. **Follow existing code style** - Match the project's current patterns
|
||||
4. **Don't break existing functionality** - Ensure all existing tests pass
|
||||
5. **Include tests** - Add or update tests that verify your fix or feature
|
||||
6. **Update documentation** - If your changes affect behavior, update relevant docs
|
||||
|
||||
## Testing
|
||||
|
||||
Ensure all tests pass by running:
|
||||
```bash
|
||||
uv run pytest tests/ -p no:warnings
|
||||
```
|
||||
|
||||
## Questions?
|
||||
|
||||
If you're unsure whether your contribution would be welcome, please open an issue to discuss it first. This saves everyone time and ensures alignment with the project's direction.
|
||||
79
Dockerfile
Normal file
@@ -0,0 +1,79 @@
|
||||
# Stage 1: Frontend build
|
||||
FROM node:21-slim AS frontend-build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy frontend package files
|
||||
COPY talemate_frontend/package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy frontend source
|
||||
COPY talemate_frontend/ ./
|
||||
|
||||
# Build frontend
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Backend build
|
||||
FROM python:3.11-slim AS backend-build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
bash \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install uv
|
||||
RUN pip install uv
|
||||
|
||||
# Copy installation files
|
||||
COPY pyproject.toml uv.lock /app/
|
||||
|
||||
# Copy the Python source code (needed for editable install)
|
||||
COPY ./src /app/src
|
||||
|
||||
# Create virtual environment and install dependencies (includes CUDA support via pyproject.toml)
|
||||
RUN uv sync
|
||||
|
||||
# Stage 3: Final image
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
bash \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install uv in the final stage
|
||||
RUN pip install uv
|
||||
|
||||
# Copy virtual environment from backend-build stage
|
||||
COPY --from=backend-build /app/.venv /app/.venv
|
||||
|
||||
# Copy Python source code
|
||||
COPY --from=backend-build /app/src /app/src
|
||||
|
||||
# Copy Node.js build artifacts from frontend-build stage
|
||||
COPY --from=frontend-build /app/dist /app/talemate_frontend/dist
|
||||
|
||||
# Copy the frontend WSGI file if it exists
|
||||
COPY frontend_wsgi.py /app/frontend_wsgi.py
|
||||
|
||||
# Copy base config
|
||||
COPY config.example.yaml /app/config.yaml
|
||||
|
||||
# Copy essentials
|
||||
COPY scenes templates chroma* /app/
|
||||
|
||||
# Set PYTHONPATH to include the src directory
|
||||
ENV PYTHONPATH=/app/src:$PYTHONPATH
|
||||
|
||||
# Make ports available to the world outside this container
|
||||
EXPOSE 5050
|
||||
EXPOSE 8080
|
||||
|
||||
# Use bash as the shell, activate the virtual environment, and run backend server
|
||||
CMD ["uv", "run", "src/talemate/server/run.py", "runserver", "--host", "0.0.0.0", "--port", "5050", "--frontend-host", "0.0.0.0", "--frontend-port", "8080"]
|
||||
191
README.md
@@ -1,167 +1,54 @@
|
||||
# Talemate
|
||||
|
||||
Allows you to play roleplay scenarios with large language models.
|
||||
Roleplay with AI with a focus on strong narration and consistent world and game state tracking.
|
||||
|
||||
It does not run any large language models itself but relies on existing APIs. Currently supports **text-generation-webui** and **openai**.
|
||||
<div align="center">
|
||||
|
||||
This means you need to either have an openai api key or know how to setup [oobabooga/text-generation-webui](https://github.com/oobabooga/text-generation-webui) (locally or remotely via gpu renting. `--extension openai` flag needs to be set)
|
||||
|<img src="docs/img/ss-1.png" width="400" height="250" alt="Screenshot 1">|<img src="docs/img/ss-2.png" width="400" height="250" alt="Screenshot 2">|
|
||||
|------------------------------------------|------------------------------------------|
|
||||
|<img src="docs/img/ss-3.png" width="400" height="250" alt="Screenshot 3">|<img src="docs/img/ss-4.png" width="400" height="250" alt="Screenshot 4">|
|
||||
|
||||
As of version 0.13.0 the legacy text-generator-webui API `--extension api` is no longer supported, please use their new `--extension openai` api implementation instead.
|
||||
</div>
|
||||
|
||||

|
||||

|
||||
## Core Features
|
||||
|
||||
## Current features
|
||||
- Multiple agents for dialogue, narration, summarization, direction, editing, world state management, character/scenario creation, text-to-speech, and visual generation
|
||||
- Supports per agent API selection
|
||||
- Long-term memory and passage of time tracking
|
||||
- Narrative world state management to reinforce character and world truths
|
||||
- Creative tools for managing NPCs, AI-assisted character, and scenario creation with template support
|
||||
- Node editor for creating complex scenarios and re-usable modules
|
||||
- Context management for character details, world information, past events, and pinned information
|
||||
- Customizable templates for all prompts using Jinja2
|
||||
- Modern, responsive UI
|
||||
|
||||
- responive modern ui
|
||||
- agents
|
||||
- conversation: handles character dialogue
|
||||
- narration: handles narrative exposition
|
||||
- summarization: handles summarization to compress context while maintain history
|
||||
- director: can be used to direct the story / characters
|
||||
- editor: improves AI responses (very hit and miss at the moment)
|
||||
- world state: generates world snapshot and handles passage of time (objects and characters)
|
||||
- creator: character / scenario creator
|
||||
- multi-client (agents can be connected to separate APIs)
|
||||
- long term memory
|
||||
- chromadb integration
|
||||
- passage of time
|
||||
- narrative world state
|
||||
- narrative tools
|
||||
- creative tools
|
||||
- AI backed character creation with template support (jinja2)
|
||||
- AI backed scenario creation
|
||||
- runpod integration
|
||||
- overridable templates for all prompts. (jinja2)
|
||||
## Documentation
|
||||
|
||||
## Planned features
|
||||
- [Installation and Getting started](https://vegu-ai.github.io/talemate/)
|
||||
- [User Guide](https://vegu-ai.github.io/talemate/user-guide/interacting/)
|
||||
|
||||
Kinda making it up as i go along, but i want to lean more into gameplay through AI, keeping track of gamestates, moving away from simply roleplaying towards a more game-ified experience.
|
||||
## Discord Community
|
||||
|
||||
In no particular order:
|
||||
Need help? Join the new [Discord community](https://discord.gg/8bGNRmFxMj)
|
||||
|
||||
- Extension support
|
||||
- modular agents and clients
|
||||
- Improved world state
|
||||
- Dynamic player choice generation
|
||||
- Better creative tools
|
||||
- node based scenario / character creation
|
||||
- Improved and consistent long term memory
|
||||
- Improved director agent
|
||||
- Right now this doesn't really work well on anything but GPT-4 (and even there it's debatable). It tends to steer the story in a way that introduces pacing issues. It needs a model that is creative but also reasons really well i think.
|
||||
- Gameplay loop governed by AI
|
||||
- objectives
|
||||
- quests
|
||||
- win / lose conditions
|
||||
- Automatic1111 client for in place visual generation
|
||||
## Supported APIs
|
||||
|
||||
# Quickstart
|
||||
- [OpenAI](https://platform.openai.com/overview)
|
||||
- [Anthropic](https://www.anthropic.com/)
|
||||
- [mistral.ai](https://mistral.ai/)
|
||||
- [Cohere](https://www.cohere.com/)
|
||||
- [Groq](https://www.groq.com/)
|
||||
- [Google Gemini](https://console.cloud.google.com/)
|
||||
- [OpenRouter](https://openrouter.ai/)
|
||||
|
||||
## Installation
|
||||
Supported self-hosted APIs:
|
||||
- [KoboldCpp](https://koboldai.org/cpp) ([Local](https://koboldai.org/cpp), [Runpod](https://koboldai.org/runpodcpp), [VastAI](https://koboldai.org/vastcpp), also includes image gen support)
|
||||
- [oobabooga/text-generation-webui](https://github.com/oobabooga/text-generation-webui) (local or with runpod support)
|
||||
- [LMStudio](https://lmstudio.ai/)
|
||||
- [TabbyAPI](https://github.com/theroyallab/tabbyAPI/)
|
||||
- [Ollama](https://ollama.com/)
|
||||
|
||||
Post [here](https://github.com/final-wombat/talemate/issues/17) if you run into problems during installation.
|
||||
|
||||
### Windows
|
||||
|
||||
1. Download and install Python 3.10 or higher from the [official Python website](https://www.python.org/downloads/windows/).
|
||||
1. Download and install Node.js from the [official Node.js website](https://nodejs.org/en/download/). This will also install npm.
|
||||
1. Download the Talemate project to your local machine. Download from [the Releases page](https://github.com/final-wombat/talemate/releases).
|
||||
1. Unpack the download and run `install.bat` by double clicking it. This will set up the project on your local machine.
|
||||
1. Once the installation is complete, you can start the backend and frontend servers by running `start.bat`.
|
||||
1. Navigate your browser to http://localhost:8080
|
||||
|
||||
### Linux
|
||||
|
||||
`python 3.10` or higher is required.
|
||||
|
||||
1. `git clone git@github.com:final-wombat/talemate`
|
||||
1. `cd talemate`
|
||||
1. `source install.sh`
|
||||
1. Start the backend: `python src/talemate/server/run.py runserver --host 0.0.0.0 --port 5050`.
|
||||
1. Open a new terminal, navigate to the `talemate_frontend` directory, and start the frontend server by running `npm run serve`.
|
||||
|
||||
## Configuration
|
||||
|
||||
### OpenAI
|
||||
|
||||
To set your openai api key, open `config.yaml` in any text editor and uncomment / add
|
||||
|
||||
```yaml
|
||||
openai:
|
||||
api_key: sk-my-api-key-goes-here
|
||||
```
|
||||
|
||||
You will need to restart the backend for this change to take effect.
|
||||
|
||||
### RunPod
|
||||
|
||||
To set your runpod api key, open `config.yaml` in any text editor and uncomment / add
|
||||
|
||||
```yaml
|
||||
runpod:
|
||||
api_key: my-api-key-goes-here
|
||||
```
|
||||
You will need to restart the backend for this change to take effect.
|
||||
|
||||
Once the api key is set Pods loaded from text-generation-webui templates (or the bloke's runpod llm template) will be autoamtically added to your client list in talemate.
|
||||
|
||||
**ATTENTION**: Talemate is not a suitable for way for you to determine whether your pod is currently running or not. **Always** check the runpod dashboard to see if your pod is running or not.
|
||||
|
||||
## Recommended Models
|
||||
(as of2023.10.25)
|
||||
|
||||
Any of the top models in any of the size classes here should work well:
|
||||
https://www.reddit.com/r/LocalLLaMA/comments/17fhp9k/huge_llm_comparisontest_39_models_tested_7b70b/
|
||||
|
||||
## Connecting to an LLM
|
||||
|
||||
On the right hand side click the "Add Client" button. If there is no button, you may need to toggle the client options by clicking this button:
|
||||
|
||||
As of version 0.13.0 the legacy text-generator-webui API `--extension api` is no longer supported, please use their new `--extension openai` api implementation instead.
|
||||
|
||||

|
||||
|
||||
### Text-generation-webui
|
||||
|
||||
In the modal if you're planning to connect to text-generation-webui, you can likely leave everything as is and just click Save.
|
||||
|
||||

|
||||
|
||||
### OpenAI
|
||||
|
||||
If you want to add an OpenAI client, just change the client type and select the apropriate model.
|
||||
|
||||

|
||||
|
||||
### Ready to go
|
||||
|
||||
You will know you are good to go when the client and all the agents have a green dot next to them.
|
||||
|
||||

|
||||
|
||||
## Load the introductory scenario "Infinity Quest"
|
||||
|
||||
Generated using talemate creative tools, mostly used for testing / demoing.
|
||||
|
||||
You can load it (and any other talemate scenarios or save files) by expanding the "Load" menu in the top left corner and selecting the middle tab. Then simple search for a partial name of the scenario you want to load and click on the result.
|
||||
|
||||

|
||||
|
||||
## Loading character cards
|
||||
|
||||
Supports both v1 and v2 chara specs.
|
||||
|
||||
Expand the "Load" menu in the top left corner and either click on "Upload a character card" or simply drag and drop a character card file into the same area.
|
||||
|
||||

|
||||
|
||||
Once a character is uploaded, talemate may actually take a moment because it needs to convert it to a talemate format and will also run additional LLM prompts to generate character attributes and world state.
|
||||
|
||||
Make sure you save the scene after the character is loaded as it can then be loaded as normal talemate scenario in the future.
|
||||
|
||||
## Further documentation
|
||||
|
||||
- Creative mode (docs WIP)
|
||||
- Prompt template overrides
|
||||
- [ChromaDB (long term memory)](docs/chromadb.md)
|
||||
- Runpod Integration
|
||||
Generic OpenAI api implementations (tested and confirmed working):
|
||||
- [DeepInfra](https://deepinfra.com/)
|
||||
- [llamacpp](https://github.com/ggerganov/llama.cpp) with the `api_like_OAI.py` wrapper
|
||||
- let me know if you have tested any other implementations and they failed / worked or landed somewhere in between
|
||||
|
||||
@@ -2,25 +2,39 @@ agents: {}
|
||||
clients: {}
|
||||
creator:
|
||||
content_context:
|
||||
- a fun and engaging slice of life story aimed at an adult audience.
|
||||
- a terrifying horror story aimed at an adult audience.
|
||||
- a thrilling action story aimed at an adult audience.
|
||||
- a mysterious adventure aimed at an adult audience.
|
||||
- an epic sci-fi adventure aimed at an adult audience.
|
||||
game:
|
||||
default_player_character:
|
||||
color: '#6495ed'
|
||||
description: a young man with a penchant for adventure.
|
||||
gender: male
|
||||
name: Elmer
|
||||
- a fun and engaging slice of life story
|
||||
- a terrifying horror story
|
||||
- a thrilling action story
|
||||
- a mysterious adventure
|
||||
- an epic sci-fi adventure
|
||||
|
||||
## Long-term memory
|
||||
|
||||
#chromadb:
|
||||
# embeddings: instructor
|
||||
# instructor_device: cuda
|
||||
# instructor_model: hkunlp/instructor-xl
|
||||
# openai_model: text-embedding-3-small
|
||||
|
||||
## Remote LLMs
|
||||
|
||||
#openai:
|
||||
# api_key: <API_KEY>
|
||||
|
||||
#runpod:
|
||||
# api_key: <API_KEY>
|
||||
# api_key: <API_KEY>
|
||||
|
||||
## TTS (Text-to-Speech)
|
||||
|
||||
#elevenlabs:
|
||||
# api_key: <API_KEY>
|
||||
|
||||
#coqui:
|
||||
# api_key: <API_KEY>
|
||||
|
||||
#tts:
|
||||
# device: cuda
|
||||
# model: tts_models/multilingual/multi-dataset/xtts_v2
|
||||
# voices:
|
||||
# - label: <name>
|
||||
# value: <path to .wav for voice sample>
|
||||
20
docker-compose.manual.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
talemate:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8080}:8080"
|
||||
- "${BACKEND_PORT:-5050}:5050"
|
||||
volumes:
|
||||
- ./config.yaml:/app/config.yaml
|
||||
- ./scenes:/app/scenes
|
||||
- ./templates:/app/templates
|
||||
- ./chroma:/app/chroma
|
||||
- ./tts:/app/tts
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- PYTHONPATH=/app/src:$PYTHONPATH
|
||||
command: ["uv", "run", "src/talemate/server/run.py", "runserver", "--host", "0.0.0.0", "--port", "5050", "--frontend-host", "0.0.0.0", "--frontend-port", "8080"]
|
||||
18
docker-compose.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
talemate:
|
||||
image: ghcr.io/vegu-ai/talemate:latest
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8080}:8080"
|
||||
- "${BACKEND_PORT:-5050}:5050"
|
||||
volumes:
|
||||
- ./config.yaml:/app/config.yaml
|
||||
- ./scenes:/app/scenes
|
||||
- ./templates:/app/templates
|
||||
- ./chroma:/app/chroma
|
||||
- ./tts:/app/tts
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- PYTHONPATH=/app/src:$PYTHONPATH
|
||||
command: ["uv", "run", "src/talemate/server/run.py", "runserver", "--host", "0.0.0.0", "--port", "5050", "--frontend-host", "0.0.0.0", "--frontend-port", "8080"]
|
||||
5
docs/.pages
Normal file
@@ -0,0 +1,5 @@
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Getting started: getting-started
|
||||
- User guide: user-guide
|
||||
- Developer guide: dev
|
||||
@@ -1,61 +0,0 @@
|
||||
# ChromaDB
|
||||
|
||||
Talemate uses ChromaDB to maintain long-term memory. The default embeddings used are really fast but also not incredibly accurate. If you want to use more accurate embeddings you can use the instructor embeddings or the openai embeddings. See below for instructions on how to enable these.
|
||||
|
||||
In my testing so far, instructor-xl has proved to be the most accurate (even more-so than openai)
|
||||
|
||||
## Local instructor embeddings
|
||||
|
||||
If you want chromaDB to use the more accurate (but much slower) instructor embeddings add the following to `config.yaml`:
|
||||
|
||||
**Note**: The `xl` model takes a while to load even with cuda. Expect a minute of loading time on the first scene you load.
|
||||
|
||||
```yaml
|
||||
chromadb:
|
||||
embeddings: instructor
|
||||
instructor_device: cpu
|
||||
instructor_model: hkunlp/instructor-xl
|
||||
```
|
||||
|
||||
### Instructor embedding models
|
||||
|
||||
- `hkunlp/instructor-base` (smallest / fastest)
|
||||
- `hkunlp/instructor-large`
|
||||
- `hkunlp/instructor-xl` (largest / slowest) - requires about 5GB of memory
|
||||
|
||||
You will need to restart the backend for this change to take effect.
|
||||
|
||||
**NOTE** - The first time you do this it will need to download the instructor model you selected. This may take a while, and the talemate backend will be un-responsive during that time.
|
||||
|
||||
Once the download is finished, if talemate is still un-responsive, try reloading the front-end to reconnect. When all fails just restart the backend as well. I'll try to make this more robust in the future.
|
||||
|
||||
### GPU support
|
||||
|
||||
If you want to use the instructor embeddings with GPU support, you will need to install pytorch with CUDA support.
|
||||
|
||||
To do this on windows, run `install-pytorch-cuda.bat` from the project directory. Then change your device in the config to `cuda`:
|
||||
|
||||
```yaml
|
||||
chromadb:
|
||||
embeddings: instructor
|
||||
instructor_device: cuda
|
||||
instructor_model: hkunlp/instructor-xl
|
||||
```
|
||||
|
||||
## OpenAI embeddings
|
||||
|
||||
First make sure your openai key is specified in the `config.yaml` file
|
||||
|
||||
```yaml
|
||||
openai:
|
||||
api_key: <your-key-here>
|
||||
```
|
||||
|
||||
Then add the following to `config.yaml` for chromadb:
|
||||
|
||||
```yaml
|
||||
chromadb:
|
||||
embeddings: openai
|
||||
```
|
||||
|
||||
**Note**: As with everything openai, using this isn't free. It's way cheaper than their text completion though. ALSO - if you send super explicit content they may flag / ban your key, so keep that in mind (i hear they usually send warnings first though), and always monitor your usage on their dashboard.
|
||||
185
docs/cleanup.py
Normal file
@@ -0,0 +1,185 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import argparse
|
||||
|
||||
|
||||
def find_image_references(md_file):
|
||||
"""Find all image references in a markdown file."""
|
||||
with open(md_file, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
pattern = r"!\[.*?\]\((.*?)\)"
|
||||
matches = re.findall(pattern, content)
|
||||
|
||||
cleaned_paths = []
|
||||
for match in matches:
|
||||
path = match.lstrip("/")
|
||||
if "img/" in path:
|
||||
path = path[path.index("img/") + 4 :]
|
||||
# Only keep references to versioned images
|
||||
parts = os.path.normpath(path).split(os.sep)
|
||||
if len(parts) >= 2 and parts[0].replace(".", "").isdigit():
|
||||
cleaned_paths.append(path)
|
||||
|
||||
return cleaned_paths
|
||||
|
||||
|
||||
def scan_markdown_files(docs_dir):
|
||||
"""Recursively scan all markdown files in the docs directory."""
|
||||
md_files = []
|
||||
for root, _, files in os.walk(docs_dir):
|
||||
for file in files:
|
||||
if file.endswith(".md"):
|
||||
md_files.append(os.path.join(root, file))
|
||||
return md_files
|
||||
|
||||
|
||||
def find_all_images(img_dir):
|
||||
"""Find all image files in version subdirectories."""
|
||||
image_files = []
|
||||
for root, _, files in os.walk(img_dir):
|
||||
# Get the relative path from img_dir to current directory
|
||||
rel_dir = os.path.relpath(root, img_dir)
|
||||
|
||||
# Skip if we're in the root img directory
|
||||
if rel_dir == ".":
|
||||
continue
|
||||
|
||||
# Check if the immediate parent directory is a version number
|
||||
parent_dir = rel_dir.split(os.sep)[0]
|
||||
if not parent_dir.replace(".", "").isdigit():
|
||||
continue
|
||||
|
||||
for file in files:
|
||||
if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif", ".svg")):
|
||||
rel_path = os.path.relpath(os.path.join(root, file), img_dir)
|
||||
image_files.append(rel_path)
|
||||
return image_files
|
||||
|
||||
|
||||
def grep_check_image(docs_dir, image_path):
|
||||
"""
|
||||
Check if versioned image is referenced anywhere using grep.
|
||||
Returns True if any reference is found, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# Split the image path to get version and filename
|
||||
parts = os.path.normpath(image_path).split(os.sep)
|
||||
version = parts[0] # e.g., "0.29.0"
|
||||
filename = parts[-1] # e.g., "world-state-suggestions-2.png"
|
||||
|
||||
# For versioned images, require both version and filename to match
|
||||
version_pattern = f"{version}.*{filename}"
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["grep", "-r", "-l", version_pattern, docs_dir],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.stdout.strip():
|
||||
print(
|
||||
f"Found reference to {image_path} with version pattern: {version_pattern}"
|
||||
)
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during grep check for {image_path}: {e}")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Find and optionally delete unused versioned images in MkDocs project"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--docs-dir", type=str, required=True, help="Path to the docs directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--img-dir", type=str, required=True, help="Path to the images directory"
|
||||
)
|
||||
parser.add_argument("--delete", action="store_true", help="Delete unused images")
|
||||
parser.add_argument(
|
||||
"--verbose", action="store_true", help="Show all found references and files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-grep", action="store_true", help="Skip the additional grep validation"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Convert paths to absolute paths
|
||||
docs_dir = os.path.abspath(args.docs_dir)
|
||||
img_dir = os.path.abspath(args.img_dir)
|
||||
|
||||
print(f"Scanning markdown files in: {docs_dir}")
|
||||
print(f"Looking for versioned images in: {img_dir}")
|
||||
|
||||
# Get all markdown files
|
||||
md_files = scan_markdown_files(docs_dir)
|
||||
print(f"Found {len(md_files)} markdown files")
|
||||
|
||||
# Collect all image references
|
||||
used_images = set()
|
||||
for md_file in md_files:
|
||||
refs = find_image_references(md_file)
|
||||
used_images.update(refs)
|
||||
|
||||
# Get all actual images (only from version directories)
|
||||
all_images = set(find_all_images(img_dir))
|
||||
|
||||
if args.verbose:
|
||||
print("\nAll versioned image references found in markdown:")
|
||||
for img in sorted(used_images):
|
||||
print(f"- {img}")
|
||||
|
||||
print("\nAll versioned images in directory:")
|
||||
for img in sorted(all_images):
|
||||
print(f"- {img}")
|
||||
|
||||
# Find potentially unused images
|
||||
unused_images = all_images - used_images
|
||||
|
||||
# Additional grep validation if not skipped
|
||||
if not args.skip_grep and unused_images:
|
||||
print("\nPerforming additional grep validation...")
|
||||
actually_unused = set()
|
||||
for img in unused_images:
|
||||
if not grep_check_image(docs_dir, img):
|
||||
actually_unused.add(img)
|
||||
|
||||
if len(actually_unused) != len(unused_images):
|
||||
print(
|
||||
f"\nGrep validation found {len(unused_images) - len(actually_unused)} additional image references!"
|
||||
)
|
||||
unused_images = actually_unused
|
||||
|
||||
# Report findings
|
||||
print("\nResults:")
|
||||
print(f"Total versioned images found: {len(all_images)}")
|
||||
print(f"Versioned images referenced in markdown: {len(used_images)}")
|
||||
print(f"Unused versioned images: {len(unused_images)}")
|
||||
|
||||
if unused_images:
|
||||
print("\nUnused versioned images:")
|
||||
for img in sorted(unused_images):
|
||||
print(f"- {img}")
|
||||
|
||||
if args.delete:
|
||||
print("\nDeleting unused versioned images...")
|
||||
for img in unused_images:
|
||||
full_path = os.path.join(img_dir, img)
|
||||
try:
|
||||
os.remove(full_path)
|
||||
print(f"Deleted: {img}")
|
||||
except Exception as e:
|
||||
print(f"Error deleting {img}: {e}")
|
||||
print("\nDeletion complete")
|
||||
else:
|
||||
print("\nNo unused versioned images found!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
docs/dev/agents/example/test/__init__.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from talemate.agents.base import Agent, AgentAction
|
||||
from talemate.agents.registry import register
|
||||
from talemate.events import GameLoopEvent
|
||||
import talemate.emit.async_signals
|
||||
from talemate.emit import emit
|
||||
|
||||
|
||||
@register()
|
||||
class TestAgent(Agent):
|
||||
agent_type = "test"
|
||||
verbose_name = "Test"
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.is_enabled = True
|
||||
self.actions = {
|
||||
"test": AgentAction(
|
||||
enabled=True,
|
||||
label="Test",
|
||||
description="Test",
|
||||
),
|
||||
}
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
return self.is_enabled
|
||||
|
||||
@property
|
||||
def has_toggle(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def experimental(self):
|
||||
return True
|
||||
|
||||
def connect(self, scene):
|
||||
super().connect(scene)
|
||||
talemate.emit.async_signals.get("game_loop").connect(self.on_game_loop)
|
||||
|
||||
async def on_game_loop(self, emission: GameLoopEvent):
|
||||
"""
|
||||
Called on the beginning of every game loop
|
||||
"""
|
||||
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
emit(
|
||||
"status",
|
||||
status="info",
|
||||
message="Annoying you with a test message every game loop.",
|
||||
)
|
||||
134
docs/dev/client/example/runpod_vllm/__init__.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
An attempt to write a client against the runpod serverless vllm worker.
|
||||
|
||||
This is close to functional, but since runpod serverless gpu availability is currently terrible, i have
|
||||
been unable to properly test it.
|
||||
|
||||
Putting it here for now since i think it makes a decent example of how to write a client against a new service.
|
||||
"""
|
||||
|
||||
import pydantic
|
||||
import structlog
|
||||
import runpod
|
||||
import asyncio
|
||||
import aiohttp
|
||||
from talemate.client.base import ClientBase, ExtraField
|
||||
from talemate.client.registry import register
|
||||
from talemate.emit import emit
|
||||
from talemate.config import Client as BaseClientConfig
|
||||
|
||||
log = structlog.get_logger("talemate.client.runpod_vllm")
|
||||
|
||||
|
||||
class Defaults(pydantic.BaseModel):
|
||||
max_token_length: int = 4096
|
||||
model: str = ""
|
||||
runpod_id: str = ""
|
||||
|
||||
|
||||
class ClientConfig(BaseClientConfig):
|
||||
runpod_id: str = ""
|
||||
|
||||
|
||||
@register()
|
||||
class RunPodVLLMClient(ClientBase):
|
||||
client_type = "runpod_vllm"
|
||||
conversation_retries = 5
|
||||
config_cls = ClientConfig
|
||||
|
||||
class Meta(ClientBase.Meta):
|
||||
title: str = "Runpod VLLM"
|
||||
name_prefix: str = "Runpod VLLM"
|
||||
enable_api_auth: bool = True
|
||||
manual_model: bool = True
|
||||
defaults: Defaults = Defaults()
|
||||
extra_fields: dict[str, ExtraField] = {
|
||||
"runpod_id": ExtraField(
|
||||
name="runpod_id",
|
||||
type="text",
|
||||
label="Runpod ID",
|
||||
required=True,
|
||||
description="The Runpod ID to connect to.",
|
||||
)
|
||||
}
|
||||
|
||||
def __init__(self, model=None, runpod_id=None, **kwargs):
|
||||
self.model_name = model
|
||||
self.runpod_id = runpod_id
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def experimental(self):
|
||||
return False
|
||||
|
||||
def set_client(self, **kwargs):
|
||||
log.debug("set_client", kwargs=kwargs, runpod_id=self.runpod_id)
|
||||
self.runpod_id = kwargs.get("runpod_id", self.runpod_id)
|
||||
|
||||
def tune_prompt_parameters(self, parameters: dict, kind: str):
|
||||
super().tune_prompt_parameters(parameters, kind)
|
||||
|
||||
keys = list(parameters.keys())
|
||||
|
||||
valid_keys = ["temperature", "top_p", "max_tokens"]
|
||||
|
||||
for key in keys:
|
||||
if key not in valid_keys:
|
||||
del parameters[key]
|
||||
|
||||
async def get_model_name(self):
|
||||
return self.model_name
|
||||
|
||||
async def generate(self, prompt: str, parameters: dict, kind: str):
|
||||
"""
|
||||
Generates text from the given prompt and parameters.
|
||||
"""
|
||||
prompt = prompt.strip()
|
||||
|
||||
self.log.debug("generate", prompt=prompt[:128] + " ...", parameters=parameters)
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
endpoint = runpod.AsyncioEndpoint(self.runpod_id, session)
|
||||
|
||||
run_request = await endpoint.run(
|
||||
{
|
||||
"input": {
|
||||
"prompt": prompt,
|
||||
}
|
||||
# "parameters": parameters
|
||||
}
|
||||
)
|
||||
|
||||
while (await run_request.status()) not in [
|
||||
"COMPLETED",
|
||||
"FAILED",
|
||||
"CANCELLED",
|
||||
]:
|
||||
status = await run_request.status()
|
||||
log.debug("generate", status=status)
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
status = await run_request.status()
|
||||
|
||||
log.debug("generate", status=status)
|
||||
|
||||
response = await run_request.output()
|
||||
|
||||
log.debug("generate", response=response)
|
||||
|
||||
return response["choices"][0]["tokens"][0]
|
||||
|
||||
except Exception as e:
|
||||
self.log.error("generate error", e=e)
|
||||
emit(
|
||||
"status", message="Error during generation (check logs)", status="error"
|
||||
)
|
||||
return ""
|
||||
|
||||
def reconfigure(self, **kwargs):
|
||||
if kwargs.get("model"):
|
||||
self.model_name = kwargs["model"]
|
||||
if "runpod_id" in kwargs:
|
||||
self.api_auth = kwargs["runpod_id"]
|
||||
self.set_client(**kwargs)
|
||||
66
docs/dev/client/example/test/__init__.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import pydantic
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from talemate.client.base import ClientBase
|
||||
from talemate.client.registry import register
|
||||
|
||||
|
||||
class Defaults(pydantic.BaseModel):
|
||||
api_url: str = "http://localhost:1234"
|
||||
max_token_length: int = 4096
|
||||
|
||||
|
||||
@register()
|
||||
class TestClient(ClientBase):
|
||||
client_type = "test"
|
||||
|
||||
class Meta(ClientBase.Meta):
|
||||
name_prefix: str = "test"
|
||||
title: str = "Test"
|
||||
defaults: Defaults = Defaults()
|
||||
|
||||
def set_client(self, **kwargs):
|
||||
self.client = AsyncOpenAI(base_url=self.api_url + "/v1", api_key="sk-1111")
|
||||
|
||||
def tune_prompt_parameters(self, parameters: dict, kind: str):
|
||||
"""
|
||||
Talemate adds a bunch of parameters to the prompt, but not all of them are valid for all clients.
|
||||
|
||||
This method is called before the prompt is sent to the client, and it allows the client to remove
|
||||
any parameters that it doesn't support.
|
||||
"""
|
||||
|
||||
super().tune_prompt_parameters(parameters, kind)
|
||||
|
||||
keys = list(parameters.keys())
|
||||
|
||||
valid_keys = ["temperature", "top_p"]
|
||||
|
||||
for key in keys:
|
||||
if key not in valid_keys:
|
||||
del parameters[key]
|
||||
|
||||
async def get_model_name(self):
|
||||
"""
|
||||
This should return the name of the model that is being used.
|
||||
"""
|
||||
|
||||
return "Mock test model"
|
||||
|
||||
async def generate(self, prompt: str, parameters: dict, kind: str):
|
||||
"""
|
||||
Generates text from the given prompt and parameters.
|
||||
"""
|
||||
human_message = {"role": "user", "content": prompt.strip()}
|
||||
|
||||
self.log.debug("generate", prompt=prompt[:128] + " ...", parameters=parameters)
|
||||
|
||||
try:
|
||||
response = await self.client.chat.completions.create(
|
||||
model=self.model_name, messages=[human_message], **parameters
|
||||
)
|
||||
|
||||
return response.choices[0].message.content
|
||||
except Exception as e:
|
||||
self.log.error("generate error", e=e)
|
||||
return ""
|
||||
166
docs/dev/howto/add-a-worldstate-template-type.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Adding a new world-state template
|
||||
|
||||
I am writing this up as I add phrase detection functionality to the `Writing Style` template, so that in the future, hopefully when new template types need to be added this document can just given to the LLM of the month, to do it.
|
||||
|
||||
## Introduction
|
||||
|
||||
World state templates are reusable components that plug in various parts of talemate.
|
||||
|
||||
At this point there are following types:
|
||||
|
||||
- Character Attribute
|
||||
- Character Detail
|
||||
- Writing Style
|
||||
- Spice (for randomization of content during generation)
|
||||
- Scene Type
|
||||
- State Reinforcement
|
||||
|
||||
Basically whenever we want to add something reusable and customizable by the user, a world state template is likely a good solution.
|
||||
|
||||
## Steps to creating a new template type
|
||||
|
||||
### 1. Add a pydantic schema (python)
|
||||
|
||||
In `src/talemate/world_state/templates` create a new `.py` file with reasonable name.
|
||||
|
||||
In this example I am extending the `Writing Style` template to include phrase detection functionality, which will be used by the `Editor` agent to detect certain phrases and then act upon them.
|
||||
|
||||
There already is a `content.py` file - so it makes sense to just add this new functionality to this file.
|
||||
|
||||
```python
|
||||
class PhraseDetection(pydantic.BaseModel):
|
||||
phrase: str
|
||||
instructions: str
|
||||
# can be "unwanted" for now, more added later
|
||||
classification: Literal["unwanted"] = "unwanted"
|
||||
|
||||
@register("writing_style")
|
||||
class WritingStyle(Template):
|
||||
description: str | None = None
|
||||
phrases: list[PhraseDetection] = pydantic.Field(default_factory=list)
|
||||
|
||||
def render(self, scene: "Scene", character_name: str):
|
||||
return self.formatted("instructions", scene, character_name)
|
||||
```
|
||||
|
||||
If I were to create a new file I'd still want to read one of the existing files first to understand imports and style.
|
||||
|
||||
### 2. Add a vue component to allow management (vue, js)
|
||||
|
||||
Next we need to add a new vue component that exposes a UX for us to manage this new template type.
|
||||
|
||||
For this I am creating `talemate_frontend/src/components/WorldStateManagerTemplateWritingStyle.vue`.
|
||||
|
||||
## Bare Minimum Understanding for New Template Components
|
||||
|
||||
When adding a new component for managing a template type, you need to understand:
|
||||
|
||||
### Component Structure
|
||||
|
||||
1. **Props**: The component always receives an `immutableTemplate` prop with the template data.
|
||||
2. **Data Management**: Create a local copy of the template data for editing before saving back.
|
||||
3. **Emits**: Use the `update` event to send modified template data back to the parent.
|
||||
|
||||
### Core Implementation Requirements
|
||||
|
||||
1. **Template Properties**: Always include fields for `name`, `description`, and `favorite` status.
|
||||
2. **Data Binding**: Implement two-way binding with `v-model` for all editable fields.
|
||||
3. **Dirty State Tracking**: Track when changes are made but not yet saved.
|
||||
4. **Save Method**: Implement a `save()` method that emits the updated template.
|
||||
|
||||
### Component Lifecycle
|
||||
|
||||
1. **Initialization**: Use the `created` hook to initialize the local template copy.
|
||||
2. **Watching for Changes**: Set up a watcher for the `immutableTemplate` to handle external updates.
|
||||
|
||||
### UI Patterns
|
||||
|
||||
1. **Forms**: Use Vuetify form components with consistent validation.
|
||||
2. **Actions**: Provide clear user actions for editing and managing template items.
|
||||
3. **Feedback**: Give visual feedback when changes are being made or saved.
|
||||
|
||||
The WorldStateManagerTemplate components follow a consistent pattern where they:
|
||||
- Display and edit general template metadata (name, description, favorite status)
|
||||
- Provide specialized UI for the template's unique properties
|
||||
- Handle the create, read, update, delete (CRUD) operations for template items
|
||||
- Maintain data integrity by properly handling template updates
|
||||
|
||||
You absolutely should read an existing component like `WorldStateManagerTemplateWritingStyle.vue` first to get a good understanding of the implementation.
|
||||
|
||||
## Integrating with WorldStateManagerTemplates
|
||||
|
||||
After creating your template component, you need to integrate it with the WorldStateManagerTemplates component:
|
||||
|
||||
### 1. Import the Component
|
||||
|
||||
Edit `talemate_frontend/src/components/WorldStateManagerTemplates.vue` and add an import for your new component:
|
||||
|
||||
```javascript
|
||||
import WorldStateManagerTemplateWritingStyle from './WorldStateManagerTemplateWritingStyle.vue'
|
||||
```
|
||||
|
||||
### 2. Register the Component
|
||||
|
||||
Add your component to the components section of the WorldStateManagerTemplates:
|
||||
|
||||
```javascript
|
||||
components: {
|
||||
// ... existing components
|
||||
WorldStateManagerTemplateWritingStyle
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Add Conditional Rendering
|
||||
|
||||
In the template section, add a new conditional block to render your component when the template type matches:
|
||||
|
||||
```html
|
||||
<WorldStateManagerTemplateWritingStyle v-else-if="template.template_type === 'writing_style'"
|
||||
:immutableTemplate="template"
|
||||
@update="(template) => applyAndSaveTemplate(template)"
|
||||
/>
|
||||
```
|
||||
|
||||
### 4. Add Icon and Color
|
||||
|
||||
Add cases for your template type in the `iconForTemplate` and `colorForTemplate` methods:
|
||||
|
||||
```javascript
|
||||
iconForTemplate(template) {
|
||||
// ... existing conditions
|
||||
else if (template.template_type == 'writing_style') {
|
||||
return 'mdi-script-text';
|
||||
}
|
||||
return 'mdi-cube-scan';
|
||||
},
|
||||
|
||||
colorForTemplate(template) {
|
||||
// ... existing conditions
|
||||
else if (template.template_type == 'writing_style') {
|
||||
return 'highlight5';
|
||||
}
|
||||
return 'grey';
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Add Help Message
|
||||
|
||||
Add a help message for your template type in the `helpMessages` object in the data section:
|
||||
|
||||
```javascript
|
||||
helpMessages: {
|
||||
// ... existing messages
|
||||
writing_style: "Writing style templates are used to define a writing style that can be applied to the generated content. They can be used to add a specific flavor or tone. A template must explicitly support writing styles to be able to use a writing style template.",
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Update Template Type Selection
|
||||
|
||||
Add your template type to the `templateTypes` array in the data section:
|
||||
|
||||
```javascript
|
||||
templateTypes: [
|
||||
// ... existing types
|
||||
{ "title": "Writing style", "value": 'writing_style'},
|
||||
]
|
||||
```
|
||||
3
docs/dev/index.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Coming soon
|
||||
|
||||
Developer documentation is coming soon. Stay tuned!
|
||||
85
docs/dev/templates.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Template Overrides
|
||||
|
||||
!!! warning "Old documentation"
|
||||
This is old documentation and needs to be updated, however may still contain useful information.
|
||||
|
||||
## Introduction to Templates
|
||||
|
||||
In Talemate, templates are used to generate dynamic content for various agents involved in roleplaying scenarios. These templates leverage the Jinja2 templating engine, allowing for the inclusion of variables, conditional logic, and custom functions to create rich and interactive narratives.
|
||||
|
||||
## Template Structure
|
||||
|
||||
A typical template in Talemate consists of several sections, each enclosed within special section tags (`<|SECTION:NAME|>` and `<|CLOSE_SECTION|>`). These sections can include character details, dialogue examples, scenario overviews, tasks, and additional context. Templates utilize loops and blocks to iterate over data and render content conditionally based on the task requirements.
|
||||
|
||||
## Overriding Templates
|
||||
|
||||
Users can customize the behavior of Talemate by overriding the default templates. To override a template, create a new template file with the same name in the `./templates/prompts/{agent}/` directory. When a custom template is present, Jinja2 will prioritize it over the default template located in the `./src/talemate/prompts/templates/{agent}/` directory.
|
||||
|
||||
## Creator Agent Templates
|
||||
|
||||
The creator agent templates allow for the creation of new characters within the character creator. Following the naming convention `character-attributes-*.jinja2`, `character-details-*.jinja2`, and `character-example-dialogue-*.jinja2`, users can add new templates that will be available for selection in the character creator.
|
||||
|
||||
### Requirements for Creator Templates
|
||||
|
||||
- All three types (`attributes`, `details`, `example-dialogue`) need to be available for a choice to be valid in the character creator.
|
||||
- Users can check the human templates for an understanding of how to structure these templates.
|
||||
|
||||
### Example Templates
|
||||
|
||||
- `src/talemate/prompts/templates/creator/character-attributes-human.jinja2`
|
||||
- `src/talemate/prompts/templates/creator/character-details-human.jinja2`
|
||||
- `src/talemate/prompts/templates/creator/character-example-dialogue-human.jinja2`
|
||||
|
||||
These example templates can serve as a guide for users to create their own custom templates for the character creator.
|
||||
|
||||
### Extending Existing Templates
|
||||
|
||||
Jinja2's template inheritance feature allows users to extend existing templates and add extra information. By using the `{% extends "template-name.jinja2" %}` tag, a new template can inherit everything from an existing template and then add or override specific blocks of content.
|
||||
|
||||
#### Example
|
||||
|
||||
To add a description of a character's hairstyle to the human character details template, you could create a new template like this:
|
||||
|
||||
```jinja2
|
||||
{% extends "character-details-human.jinja2" %}
|
||||
{% block questions %}
|
||||
{% if character_details.q("what does "+character.name+"'s hair look like?") -%}
|
||||
Briefly describe {{ character.name }}'s hair-style using a narrative writing style that reminds of mid 90s point and click adventure games. (2 - 3 sentences).
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
This example shows how to extend the `character-details-human.jinja2` template and add a block for questions about the character's hair. The `{% block questions %}` tag is used to define a section where additional questions can be inserted or existing ones can be overridden.
|
||||
|
||||
## Advanced Template Topics
|
||||
|
||||
### Jinja2 Functions in Talemate
|
||||
|
||||
Talemate exposes several functions to the Jinja2 template environment, providing utilities for data manipulation, querying, and controlling content flow. Here's a list of available functions:
|
||||
|
||||
1. `set_prepared_response(response, prepend)`: Sets the prepared response with an optional prepend string. This function allows the template to specify the beginning of the LLM response when processing the rendered template. For example, `set_prepared_response("Certainly!")` will ensure that the LLM's response starts with "Certainly!".
|
||||
2. `set_prepared_response_random(responses, prefix)`: Chooses a random response from a list and sets it as the prepared response with an optional prefix.
|
||||
3. `set_eval_response(empty)`: Prepares the response for evaluation, optionally initializing a counter for an empty string.
|
||||
4. `set_json_response(initial_object, instruction, cutoff)`: Prepares for a JSON response with an initial object and optional instruction and cutoff.
|
||||
5. `set_question_eval(question, trigger, counter, weight)`: Sets up a question for evaluation with a trigger, counter, and weight.
|
||||
6. `disable_dedupe()`: Disables deduplication of the response text.
|
||||
7. `random(min, max)`: Generates a random integer between the specified minimum and maximum.
|
||||
8. `query_scene(query, at_the_end, as_narrative)`: Queries the scene with a question and returns the formatted response.
|
||||
9. `query_text(query, text, as_question_answer)`: Queries a text with a question and returns the formatted response.
|
||||
10. `query_memory(query, as_question_answer, **kwargs)`: Queries the memory with a question and returns the formatted response.
|
||||
11. `instruct_text(instruction, text)`: Instructs the text with a command and returns the result.
|
||||
12. `retrieve_memories(lines, goal)`: Retrieves memories based on the provided lines and an optional goal.
|
||||
13. `uuidgen()`: Generates a UUID string.
|
||||
14. `to_int(x)`: Converts the given value to an integer.
|
||||
15. `config`: Accesses the configuration settings.
|
||||
16. `len(x)`: Returns the length of the given object.
|
||||
17. `count_tokens(x)`: Counts the number of tokens in the given text.
|
||||
18. `print(x)`: Prints the given object (mainly for debugging purposes).
|
||||
|
||||
These functions enhance the capabilities of templates, allowing for dynamic and interactive content generation.
|
||||
|
||||
### Error Handling
|
||||
|
||||
Errors encountered during template rendering are logged and propagated to the user interface. This ensures that users are informed of any issues that may arise, allowing them to troubleshoot and resolve problems effectively.
|
||||
|
||||
By following these guidelines, users can create custom templates that tailor the Talemate experience to their specific storytelling needs.# Template Overrides in Talemate
|
||||
14
docs/dev/third-party-reference.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## Third Party API docs
|
||||
|
||||
### Chat completions
|
||||
|
||||
- [Anthropic](https://docs.anthropic.com/en/api/messages)
|
||||
- [Cohere](https://docs.cohere.com/reference/chat)
|
||||
- [Google AI](https://ai.google.dev/api/generate-content#v1beta.GenerationConfig)
|
||||
- [Groq](https://console.groq.com/docs/api-reference#chat-create)
|
||||
- [KoboldCpp](https://lite.koboldai.net/koboldcpp_api#/api/v1)
|
||||
- [LMStudio](https://lmstudio.ai/docs/api/rest-api)
|
||||
- [Mistral AI](https://docs.mistral.ai/api/)
|
||||
- [OpenAI](https://platform.openai.com/docs/api-reference/completions)
|
||||
- [TabbyAPI](https://theroyallab.github.io/tabbyAPI/#operation/chat_completion_request_v1_chat_completions_post)
|
||||
- [Text-Generation-WebUI](https://github.com/oobabooga/text-generation-webui/blob/main/extensions/openai/typing.py)
|
||||
5
docs/getting-started/.pages
Normal file
@@ -0,0 +1,5 @@
|
||||
nav:
|
||||
- 1. Installation: installation
|
||||
- 2. Connect a client: connect-a-client.md
|
||||
- 3. Load a scene: load-a-scene.md
|
||||
- ...
|
||||
3
docs/getting-started/advanced/.pages
Normal file
@@ -0,0 +1,3 @@
|
||||
nav:
|
||||
- change-host-and-port.md
|
||||
- ...
|
||||
99
docs/getting-started/advanced/change-host-and-port.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Changing host and port
|
||||
|
||||
## Backend
|
||||
|
||||
By default, the backend listens on `localhost:5050`.
|
||||
|
||||
To run the server on a different host and port, you need to change the values passed to the `--host` and `--port` parameters during startup and also make sure the frontend knows the new values.
|
||||
|
||||
### Changing the host and port for the backend
|
||||
|
||||
#### :material-linux: Linux
|
||||
|
||||
Copy `start.sh` to `start_custom.sh` and edit the `--host` and `--port` parameters.
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
uv run src/talemate/server/run.py runserver --host 0.0.0.0 --port 1234
|
||||
```
|
||||
|
||||
#### :material-microsoft-windows: Windows
|
||||
|
||||
Copy `start.bat` to `start_custom.bat` and edit the `--host` and `--port` parameters.
|
||||
|
||||
```batch
|
||||
uv run src\talemate\server\run.py runserver --host 0.0.0.0 --port 1234
|
||||
```
|
||||
|
||||
### Letting the frontend know about the new host and port
|
||||
|
||||
Copy `talemate_frontend/example.env.development.local` to `talemate_frontend/.env.production.local` and edit the `VITE_TALEMATE_BACKEND_WEBSOCKET_URL`.
|
||||
|
||||
```env
|
||||
VITE_TALEMATE_BACKEND_WEBSOCKET_URL=ws://localhost:1234
|
||||
```
|
||||
|
||||
Next rebuild the frontend.
|
||||
|
||||
```bash
|
||||
cd talemate_frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Start the backend and frontend
|
||||
|
||||
Start the backend and frontend as usual.
|
||||
|
||||
#### :material-linux: Linux
|
||||
|
||||
```bash
|
||||
./start_custom.sh
|
||||
```
|
||||
|
||||
#### :material-microsoft-windows: Windows
|
||||
|
||||
```batch
|
||||
start_custom.bat
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
By default, the frontend listens on `localhost:8080`.
|
||||
|
||||
To change the frontend host and port, you need to change the values passed to the `--frontend-host` and `--frontend-port` parameters during startup.
|
||||
|
||||
### Changing the host and port for the frontend
|
||||
|
||||
#### :material-linux: Linux
|
||||
|
||||
Copy `start.sh` to `start_custom.sh` and edit the `--frontend-host` and `--frontend-port` parameters.
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
uv run src/talemate/server/run.py runserver --host 0.0.0.0 --port 5055 \
|
||||
--frontend-host localhost --frontend-port 8082
|
||||
```
|
||||
|
||||
#### :material-microsoft-windows: Windows
|
||||
|
||||
Copy `start.bat` to `start_custom.bat` and edit the `--frontend-host` and `--frontend-port` parameters.
|
||||
|
||||
```batch
|
||||
uv run src\talemate\server\run.py runserver --host 0.0.0.0 --port 5055 --frontend-host localhost --frontend-port 8082
|
||||
```
|
||||
|
||||
### Start the backend and frontend
|
||||
|
||||
Start the backend and frontend as usual.
|
||||
|
||||
#### :material-linux: Linux
|
||||
|
||||
```bash
|
||||
./start_custom.sh
|
||||
```
|
||||
|
||||
#### :material-microsoft-windows: Windows
|
||||
|
||||
```batch
|
||||
start_custom.bat
|
||||
```
|
||||
68
docs/getting-started/connect-a-client.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Connect a client
|
||||
|
||||
Once Talemate is up and running and you are connected, you will see a notification in the corner instructing you to configured a client.
|
||||
|
||||

|
||||
|
||||
Talemate uses client(s) to connect to local or remote AI text generation APIs like koboldcpp, text-generation-webui or OpenAI.
|
||||
|
||||
## Add a new client
|
||||
|
||||
On the right hand side click the **:material-plus-box: ADD CLIENT** button.
|
||||
|
||||

|
||||
|
||||
!!! note "No button?"
|
||||
If there is no button, you may need to toggle the client options by clicking this button
|
||||
|
||||

|
||||
|
||||
The client configuration window will appear. Here you can choose the type of client you want to add.
|
||||
|
||||

|
||||
|
||||
## Choose an API / Client Type
|
||||
|
||||
We have support for multiple local and remote APIs. You can choose to use one or more of them.
|
||||
|
||||
!!! note "Local vs remote"
|
||||
A local API runs on your machine, while a remote API runs on a server somewhere else.
|
||||
|
||||
Select the API you want to use and click through to follow the instructions to configure a client for it:
|
||||
|
||||
##### Remote APIs
|
||||
|
||||
- [OpenAI](/talemate/user-guide/clients/types/openai/)
|
||||
- [Anthropic](/talemate/user-guide/clients/types/anthropic/)
|
||||
- [mistral.ai](/talemate/user-guide/clients/types/mistral/)
|
||||
- [Cohere](/talemate/user-guide/clients/types/cohere/)
|
||||
- [Groq](/talemate/user-guide/clients/types/groq/)
|
||||
- [Google Gemini](/talemate/user-guide/clients/types/google/)
|
||||
|
||||
##### Local APIs
|
||||
|
||||
- [KoboldCpp](/talemate/user-guide/clients/types/koboldcpp/)
|
||||
- [Text-Generation-WebUI](/talemate/user-guide/clients/types/text-generation-webui/)
|
||||
- [LMStudio](/talemate/user-guide/clients/types/lmstudio/)
|
||||
- [TabbyAPI](/talemate/user-guide/clients/types/tabbyapi/)
|
||||
|
||||
##### Unofficial OpenAI API implementations
|
||||
|
||||
- [DeepInfra](/talemate/user-guide/clients/types/openai-compatible/#deepinfra)
|
||||
- llamacpp with the `api_like_OAI.py` wrapper
|
||||
|
||||
## Assign the client to the agents
|
||||
|
||||
Whenever you add your first client, Talemate will automatically assign it to all agents. Once the client is configured and assigned, all agents should have a green dot next to them. (Or grey if the agent is currently disabled)
|
||||
|
||||

|
||||
|
||||
You can tell the client is assigned to the agent by checking the tag beneath the agent name, which will contain the client name if it is assigned.
|
||||
|
||||

|
||||
|
||||
## Its not assigned!
|
||||
|
||||
If for some reason the client is not assigned to the agent, you can manually assign it to all agents by clicking the **:material-transit-connection-variant: Assign to all agents** button.
|
||||
|
||||

|
||||
5
docs/getting-started/installation/.pages
Normal file
@@ -0,0 +1,5 @@
|
||||
nav:
|
||||
- windows.md
|
||||
- linux.md
|
||||
- docker.md
|
||||
- ...
|
||||
15
docs/getting-started/installation/docker.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## Quick install instructions
|
||||
|
||||
1. `git clone https://github.com/vegu-ai/talemate.git`
|
||||
1. `cd talemate`
|
||||
1. copy config file
|
||||
1. linux: `cp config.example.yaml config.yaml`
|
||||
1. windows: `copy config.example.yaml config.yaml` (or just copy the file and rename it via the file explorer)
|
||||
1. `docker compose up`
|
||||
1. Navigate your browser to http://localhost:8080
|
||||
|
||||
!!! info "Pre-built Images"
|
||||
The default setup uses pre-built images from GitHub Container Registry that include CUDA support by default. To manually build the container instead, use `docker compose -f docker-compose.manual.yml up --build`.
|
||||
|
||||
!!! note
|
||||
When connecting local APIs running on the hostmachine (e.g. text-generation-webui), you need to use `host.docker.internal` as the hostname.
|
||||
44
docs/getting-started/installation/linux.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## Quick install instructions
|
||||
|
||||
### Dependencies
|
||||
|
||||
--8<-- "docs/snippets/common.md:python-versions"
|
||||
|
||||
1. node.js and npm - see instructions [here](https://nodejs.org/en/download/package-manager/)
|
||||
1. python- see instructions [here](https://www.python.org/downloads/)
|
||||
1. uv - see instructions [here](https://github.com/astral-sh/uv#installation)
|
||||
|
||||
### Installation
|
||||
|
||||
1. `git clone https://github.com/vegu-ai/talemate.git`
|
||||
1. `cd talemate`
|
||||
1. `source install.sh`
|
||||
- When asked if you want to install pytorch with CUDA support choose `y` if you have
|
||||
a CUDA compatible Nvidia GPU and have installed the necessary drivers.
|
||||
1. `source start.sh`
|
||||
|
||||
If everything went well, you can proceed to [connect a client](../../connect-a-client).
|
||||
|
||||
## Additional Information
|
||||
|
||||
### Setting Up a Virtual Environment
|
||||
|
||||
1. Open a terminal.
|
||||
2. Navigate to the project directory.
|
||||
3. uv will automatically create a virtual environment when you run `uv venv`.
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
1. Use uv to install dependencies by running `uv pip install -e ".[dev]"`.
|
||||
|
||||
### Running the Backend
|
||||
|
||||
1. You can start the backend server using `uv run src/talemate/server/run.py runserver --host 0.0.0.0 --port 5050`.
|
||||
|
||||
### Running the Frontend
|
||||
|
||||
1. Navigate to the `talemate_frontend` directory.
|
||||
2. If you haven't already, install npm dependencies by running `npm install`.
|
||||
3. Start the server with `npm run serve`.
|
||||
|
||||
Please note that you may need to set environment variables or modify the host and port as per your setup. You can refer to the various start scripts for more details.
|
||||
25
docs/getting-started/installation/troubleshoot.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Common issues
|
||||
|
||||
## Windows
|
||||
|
||||
### Frontend fails with errors
|
||||
|
||||
- ensure none of the directories leading to your talemate directory have special characters in them, this can cause issues with the frontend. so no `(1)` in the directory name.
|
||||
|
||||
## Docker
|
||||
|
||||
### Docker has created `config.yaml` directory
|
||||
|
||||
If you do not copy the example config to `config.yaml` before running `docker compose up` docker will create a `config` directory in the root of the project. This will cause the backend to fail to start.
|
||||
|
||||
This happens because we mount the config file directly as a docker volume, and if it does not exist docker will create a directory with the same name.
|
||||
|
||||
This will eventually be fixed, for now please make sure to copy the example config file before running the docker compose command.
|
||||
|
||||
## General
|
||||
|
||||
### Running behind reverse proxy with ssl
|
||||
|
||||
Personally i have not been able to make this work yet, but its on my list, issue stems from some vue oddities when specifying the base urls while running in a dev environment. I expect once i start building the project for production this will be resolved.
|
||||
|
||||
If you do make it work, please reach out to me so i can update this documentation.
|
||||
32
docs/getting-started/installation/windows.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## Quick install instructions
|
||||
|
||||
1. Download the latest Talemate release ZIP from the [Releases page](https://github.com/vegu-ai/talemate/releases) and extract it anywhere on your system (for example, `C:\Talemate`).
|
||||
2. Double-click **`start.bat`**.
|
||||
- On the very first run Talemate will automatically:
|
||||
1. Download a portable build of Python 3 and Node.js (no global installs required).
|
||||
2. Create and configure a Python virtual environment.
|
||||
3. Install all back-end and front-end dependencies with the included *uv* and *npm*.
|
||||
4. Build the web client.
|
||||
3. When the console window prints **"Talemate is now running"** and the logo appears, open your browser at **http://localhost:8080**.
|
||||
|
||||
!!! note "First start can take a while"
|
||||
The initial download and dependency installation may take several minutes, especially on slow internet connections. The console will keep you updated – just wait until the Talemate logo shows up.
|
||||
|
||||
### Optional: CUDA support
|
||||
|
||||
If you have an NVIDIA GPU and want CUDA acceleration for larger embedding models:
|
||||
|
||||
1. Close Talemate (if it is running).
|
||||
2. Double-click **`install-cuda.bat`**. This script swaps the CPU-only Torch build for the CUDA 12.8 build.
|
||||
3. Start Talemate again via **`start.bat`**.
|
||||
|
||||
## Maintenance & advanced usage
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| **`start.bat`** | Primary entry point – performs the initial install if needed and then starts Talemate. |
|
||||
| **`install.bat`** | Runs the installer without launching the server. Useful for automated setups or debugging. |
|
||||
| **`install-cuda.bat`** | Installs the CUDA-enabled Torch build (run after the regular install). |
|
||||
| **`update.bat`** | Pulls the latest changes from GitHub, updates dependencies, rebuilds the web client. |
|
||||
|
||||
No system-wide Python or Node.js is required – Talemate uses the embedded runtimes it downloads automatically.
|
||||
57
docs/getting-started/load-a-scene.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Load a scenario
|
||||
|
||||
Once you've set up a client and assigned it to all the agents, you will be presented with the `Home` screen. From here, you can load talemate scenarios and upload character cards.
|
||||
|
||||
To load the introductory `Infinity Quest` scenario, simply click on its entry in the `Quick Load` section.
|
||||
|
||||

|
||||
|
||||
!!! info "First time may take a moment"
|
||||
When you load the a scenario for the first time, Talemate will need to initialize the long term memory model. Which likely means a download. Just be patient and it will be ready soon.
|
||||
|
||||
## Interacting with the scenario
|
||||
|
||||
After a moment of loading, you will see the scenario's introductory message and be able to send a text interaction.
|
||||
|
||||

|
||||
|
||||
Its time to send the first message.
|
||||
|
||||
Spoken words should go into `"` and actions should be written in `*`. Talemate will automatically supply the other if you supply one.
|
||||
|
||||

|
||||
|
||||
Once sent, its now the AI's turn to respond - depending on the service and model selected this can take a a moment.
|
||||
|
||||

|
||||
|
||||
## Quick overview of UI elements
|
||||
|
||||
### Scenario tools
|
||||
|
||||
Above the chat input there is a set of tools to help you interact with the scenario.
|
||||
|
||||

|
||||
|
||||
These contain tools to, for example:
|
||||
|
||||
- regenrate the most recent AI response
|
||||
- give directions to characters
|
||||
- narrate the scene
|
||||
- advance time
|
||||
- save the current scene state
|
||||
- and more ...
|
||||
|
||||
A full guide can be found in the [Scenario Tools](/talemate/user-guide/scenario-tools) section of the user guide.
|
||||
|
||||
### World state
|
||||
|
||||
Shows a sumamrization of the current scene state.
|
||||
|
||||

|
||||
|
||||
Each item can be expanded for more information.
|
||||
|
||||

|
||||
|
||||
Find out more about the world state in the [World State](/talemate/user-guide/world-state) section of the user guide.
|
||||
BIN
docs/img/0.20.0/comfyui-base-workflow.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
docs/img/0.20.0/visual-queue.png
Normal file
|
After Width: | Height: | Size: 933 KiB |
BIN
docs/img/0.20.0/visualize-scene-tools.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/0.20.0/visualizer-busy.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
docs/img/0.20.0/visualze-new-images.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
docs/img/0.26.0/agent-disabled.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
docs/img/0.26.0/agent-enabled.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
docs/img/0.26.0/agent-has-client-assigned.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
docs/img/0.26.0/anthropic-settings.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/img/0.26.0/auto-progress-off.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
docs/img/0.26.0/autosave-blocked.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
docs/img/0.26.0/autosave-disabled.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
docs/img/0.26.0/autosave-enabled.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
docs/img/0.26.0/client-anthropic-no-api-key.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
docs/img/0.26.0/client-anthropic-ready.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
docs/img/0.26.0/client-anthropic.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/img/0.26.0/client-assigned-prompt-template.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/img/0.26.0/client-cohere-no-api-key.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
docs/img/0.26.0/client-cohere-ready.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
docs/img/0.26.0/client-cohere.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/0.26.0/client-deepinfra-ready.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
docs/img/0.26.0/client-deepinfra.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/img/0.26.0/client-google-creds-missing.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
docs/img/0.26.0/client-google-ready.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
docs/img/0.26.0/client-google.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/img/0.26.0/client-groq-no-api-key.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
docs/img/0.26.0/client-groq-ready.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
docs/img/0.26.0/client-groq.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/0.26.0/client-hibernate-1.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/img/0.26.0/client-hibernate-2.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
docs/img/0.26.0/client-koboldcpp-could-not-connect.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
docs/img/0.26.0/client-koboldcpp-ready.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
docs/img/0.26.0/client-koboldcpp.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/0.26.0/client-lmstudio-could-not-connect.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
docs/img/0.26.0/client-lmstudio-ready.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
docs/img/0.26.0/client-lmstudio.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/img/0.26.0/client-mistral-no-api-key.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
docs/img/0.26.0/client-mistral-ready.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
docs/img/0.26.0/client-mistral.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/img/0.26.0/client-ooba-could-not-connect.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
docs/img/0.26.0/client-ooba-no-model-loaded.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
docs/img/0.26.0/client-ooba-ready.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
docs/img/0.26.0/client-ooba.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/img/0.26.0/client-openai-no-api-key.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
docs/img/0.26.0/client-openai-ready.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
docs/img/0.26.0/client-openai.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/0.26.0/client-tabbyapi-could-not-connect.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
docs/img/0.26.0/client-tabbyapi-ready.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
docs/img/0.26.0/client-tabbyapi.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
docs/img/0.26.0/client-unknown-prompt-template-modal.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/img/0.26.0/client-unknown-prompt-template.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/0.26.0/cohere-settings.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/img/0.26.0/connect-a-client-add-client.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
docs/img/0.26.0/connect-a-client-assign-to-all-agents.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
docs/img/0.26.0/create-new-scene-test.png
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
docs/img/0.26.0/create-new-scene.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
docs/img/0.26.0/elevenlabs-ready.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
docs/img/0.26.0/elevenlabs-settings.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/img/0.26.0/getting-started-first-ai-response.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
docs/img/0.26.0/getting-started-first-interaction.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/img/0.26.0/getting-started-load-screen.png
Normal file
|
After Width: | Height: | Size: 634 KiB |
BIN
docs/img/0.26.0/getting-started-scene-1.png
Normal file
|
After Width: | Height: | Size: 441 KiB |
BIN
docs/img/0.26.0/getting-started-ui-element-tools.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
docs/img/0.26.0/getting-started-world-state-1.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
docs/img/0.26.0/getting-started-world-state-2.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/img/0.26.0/google-settings.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
docs/img/0.26.0/groq-settings.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
docs/img/0.26.0/interacting-input-act-as-character.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |