Skip to main content

thalovant-skillkit

Write a Thalovant skill without writing the plumbing.

pip install thalovant-skillkit
thalovant-skillkit new garden-watering

That writes a complete skill — package, locales, tests, packaging, CI — that passes its own checks before you touch it.

The skill

from thalovant_skillkit.skill import ThalovantFallbackSkill


class GardenWateringSkill(ThalovantFallbackSkill):
    FALLBACK_PRIORITY = 98

    def can_answer(self, message) -> bool:
        return self.mentions(self.utterance(message), "GardenWateringKeyword")

    def reply(self, utterance, lang, context):
        return self.dialog("garden.watering", lang)

reply is the one method most skills need. What it returns is spoken on the hub and shown in the showroom, so the two cannot drift apart.

A test

from thalovant_skillkit.testing import message

def test_it_hears_its_keyword():
    assert GardenWateringSkill().can_answer(message("water the garden"))

def test_it_ignores_the_rest():
    assert not GardenWateringSkill().can_answer(message("set a timer"))

Keeping it right

thalovant-skillkit check

Every locale complete, placeholders matching, packaging sound, priority in band — and your sentences against every other skill's. Every skill's intents are trained into one classifier on the hub, so a sentence you publish must not already be another skill's: the check compares your .intent files with the fleet's model on the Hugging Face Hub (thalovant/thalovant-m2v-intents, public). A sentence another skill already publishes fails, on the line, naming the owner; a sentence the classifier reads as another skill's warns, with its confidence. The same check runs in the CI the scaffold writes for you, on every push, and tells you which line to change. Offline, it says so and checks the rest.

What you get

self.utterance(msg) what was said
self.lang_of(msg) the language of this utterance
self.location_of(msg) where the house is
self.mentions(text, "Voc") does the text mention this vocabulary — plurals included
self.dialog("name", lang) a line from locale/<lang>/dialog/name.dialog
self.setting("key", default) one skill setting
self.reply(utterance, lang, ctx) your answer; spoken and previewed from one place

There are four bases: ThalovantSkill for a skill with its own intents, ThalovantFallbackSkill for one that answers what no intent claimed, ThalovantConversationalSkill for one that keeps a conversation going, and ThalovantCommonPlaySkill for one that answers OCP searches.

Everything on OVOSSkill still works — self.speak, self.speak_dialog, self.voc_match, the intent decorators. Nothing here replaces them.

Bounded conversation state

SkillKit 0.10 adds optional storage for application state. OVOS still owns session IDs, conversation activation, scheduling, and playback.

from thalovant_skillkit.sessions import SessionStateStore

states = SessionStateStore(max_entries=128, default_ttl=90)
states.set(session_id, {"round": 0})
with states.lock:
    game = states.get(session_id)
    if game is not None:
        game["round"] += 1
        states.set(session_id, game)  # explicitly refresh the idle timeout
states.remove(session_id)

Expiry uses a monotonic clock and is lazy. Reads do not extend a session; writes refresh its expiry and its eviction position. Capacity evicts the oldest write. Use default_ttl=None for state that should expire only on explicit removal or capacity eviction. remove(key, expected=value) protects cleanup based on an old snapshot from removing a replacement. clear() closes all stored state. Store locking is per instance, and values remain mutable: hold states.lock for compound updates. Keep blocking work outside that lock. The skill decides whether anonymous sessions are allowed and when game turn budgets are exhausted; the store does not infer identity or game rules.

Integration tests with OVOS

python -m pip install 'thalovant-skillkit[testing]'

The optional testing extra targets the tested OVOS alpha stack and supplies OvoScope. Existing testing.message and the recording testing.FakeBus remain available for small unit tests. For actual dispatch and scheduling:

from thalovant_skillkit.testing_ovos import skill_harness

with skill_harness(MySkill, skill_id="my-skill.example") as harness:
    # harness.skill uses the upstream dispatching FakeBus and real scheduler.
    harness.bus.emit(request)

skill_harness owns temporary settings, a native scheduler service, and cleanup, even when a test fails. The scheduler answers requests without a timer thread by default. isolated_xdg() restores environment variables and removes its owned temporary directories; enter it before importing skills, usually from pytest_configure. Upstream pytest plugins may already have imported OVOS, so retain MiniCroft's configuration isolation as well.

managed_minicroft(skill_ids, **options) guarantees teardown. capture_turn(croft, message, timeout=30) requires completion, detaches capture listeners in finally, and returns recorded messages, spoken text, and the originating session's latest state. Use that returned session for subsequent turns. Other speakers' session updates are excluded. The opt-in deferred_capture_gc() context scopes the documented OvoScope 1.8.5a1/pyee 12.1.1 finalizer workaround; other versions keep their normal GC policy.

Check the built distributions

check keeps its fast source-contract and fleet checks. After building, validate what will actually be installed:

python -m build
thalovant-skillkit check-artifacts --help

The stdlib-only API supports wheel and source-distribution checks together:

from thalovant_skillkit.artifacts import check_artifacts

problems = check_artifacts(
    ".", wheel="dist/my_skill-1.0.0-py3-none-any.whl",
    sdist="dist/my_skill-1.0.0.tar.gz", package_dirs=["my_skill"],
    source_paths=["scripts/audio_sources"],
    wheel_excludes=["scripts/audio_sources/"],
)
assert not problems, problems

Checks compare bundled bytes with the source, reject missing resources and unsafe or duplicate archive members, and verify matching distribution identities. source_paths adds source-only rebuild inputs. Keep domain-specific asset counts in the skill. Also install the wheel in a clean environment and resolve its opm.skill entry point from outside the checkout; archive checks do not substitute for plugin loading.


Guide: docs.thalovant.com/developers/writing-a-skill

Release files for thalovant-skillkit 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for thalovant-skillkit 0.10.0
File Size Uploaded
thalovant_skillkit-0.10.0.tar.gz 84.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for thalovant-skillkit 0.10.0
File Interpreter ABI Platform
thalovant_skillkit-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 147.3 kB

Release files / thalovant_skillkit-0.10.0.tar.gz

Download URL thalovant_skillkit-0.10.0.tar.gz
Size 84.4 kB
Tags Source
SHA-256 checksum
How to use checksums
26af9885629908ce2d7109defde9a2d9adabb8be747deaf81f20af191bfee6fc
BLAKE2b-256 checksum
How to use checksums
8aa4d134a4d29124b58dcc1d95dcee36a69ee7929901589503987d77218c0e61
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / thalovant_skillkit-0.10.0-py3-none-any.whl

Download URL thalovant_skillkit-0.10.0-py3-none-any.whl
Size 62.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d6a37956ed31119ca013930891403d61e66b072879308e802fddb7ea1615146d
BLAKE2b-256 checksum
How to use checksums
865488ad872c0f79389e3b03fea68187aefe4f468f1f40e71f298d27662a07f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release history Release notifications | RSS feed

0.21.1

2 release files

0.21.0

2 release files

0.20.0

2 release files

0.19.0

2 release files

0.18.0

2 release files

0.17.0

2 release files

0.16.1

2 release files

0.16.0

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.3

2 release files

0.11.2

2 release files

0.11.1

2 release files

0.11.0

2 release files

This release

0.10.0 This release

2 release files

0.9.0

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page