livekit-plugins-chariot 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ # Copyright 2026 LiveKit, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Chariot plugin for LiveKit Agents
16
+
17
+ Support for speech-to-text and text-to-speech with [Chariot](https://chariot.in/), for English
18
+ and Indian languages, including code-mixed speech.
19
+
20
+ See https://docs.chariot.in/api-reference/introduction for more information.
21
+ """
22
+
23
+ from .models import STTLanguages, STTModels, STTModes, STTStyles, TTSModels
24
+ from .stt import STT, SpeechStream, VADOptions
25
+ from .tts import TTS, ChunkedStream, SynthesizeStream
26
+ from .version import __version__
27
+
28
+ __all__ = [
29
+ "STT",
30
+ "SpeechStream",
31
+ "VADOptions",
32
+ "TTS",
33
+ "ChunkedStream",
34
+ "SynthesizeStream",
35
+ "STTLanguages",
36
+ "STTModels",
37
+ "STTModes",
38
+ "STTStyles",
39
+ "TTSModels",
40
+ "__version__",
41
+ ]
42
+
43
+ from livekit.agents import Plugin
44
+
45
+ from .log import logger
46
+
47
+
48
+ class ChariotPlugin(Plugin):
49
+ def __init__(self) -> None:
50
+ super().__init__(__name__, __version__, __package__, logger)
51
+
52
+
53
+ Plugin.register_plugin(ChariotPlugin())
54
+
55
+ # Cleanup docs of unexported modules
56
+ _module = dir()
57
+ NOT_IN_ALL = [m for m in _module if m not in __all__]
58
+
59
+ __pdoc__ = {}
60
+
61
+ for n in NOT_IN_ALL:
62
+ __pdoc__[n] = False
@@ -0,0 +1,116 @@
1
+ # Copyright 2026 LiveKit, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import os
19
+
20
+ import aiohttp
21
+
22
+ from livekit.agents import APIConnectionError, APIError, APIStatusError
23
+
24
+ from .version import __version__
25
+
26
+ BASE_URL = "https://api.chariot.in"
27
+ API_KEY_HEADER = "chariotai-api-key"
28
+ USER_AGENT = f"LiveKit Agents Chariot Plugin/{__version__}"
29
+
30
+ # The WebSocket close codes the API uses, besides 1000 (normal).
31
+ CLOSE_POLICY = 1008 # invalid key or parameters, idle timeout, a backlog limit
32
+ CLOSE_SERVER_ERROR = 1011
33
+ CLOSE_NO_CREDITS = 4402
34
+ CLOSE_SESSION_LIMIT = 4413 # the session reached its maximum length
35
+ CLOSE_BUSY = 4429 # every concurrent session the plan allows is in use
36
+
37
+ # HTTP status for each WebSocket error code, so retries follow the usual 4xx/5xx rules.
38
+ _ERROR_STATUS = {
39
+ "bad_request": 400,
40
+ "unknown_context": 400,
41
+ "context_closing": 400,
42
+ "session_closing": 400,
43
+ "text_too_long": 400,
44
+ "insufficient_credits": 402,
45
+ "forbidden": 403,
46
+ "idle_timeout": 408,
47
+ "voice_unavailable": 422,
48
+ "context_limit": 429,
49
+ "concurrency_limit": 429,
50
+ "upstream_error": 502,
51
+ }
52
+
53
+ # 1008 reasons that a fresh connection clears, as opposed to a refusal it would repeat.
54
+ _TRANSIENT_POLICY_REASONS = ("idle", "pending audio", "pending commit")
55
+
56
+
57
+ def resolve_api_key(api_key: str | None) -> str:
58
+ api_key = api_key or os.environ.get("CHARIOT_API_KEY")
59
+ if not api_key:
60
+ raise ValueError("Chariot API key required. Set CHARIOT_API_KEY or provide api_key.")
61
+ return api_key
62
+
63
+
64
+ def headers(api_key: str) -> dict[str, str]:
65
+ return {API_KEY_HEADER: api_key, "User-Agent": USER_AGENT}
66
+
67
+
68
+ def ws_base_url(base_url: str) -> str:
69
+ return base_url.replace("https://", "wss://", 1).replace("http://", "ws://", 1)
70
+
71
+
72
+ def error_event(event: dict) -> APIError:
73
+ """An `error` message on a WebSocket.
74
+
75
+ A string `code` names a refused request on a connection that stays open. A numeric `code`
76
+ is the close code of a connection the server is about to close.
77
+ """
78
+ code = event.get("code")
79
+ message = str(event.get("message") or "Chariot API error")
80
+ if isinstance(code, int):
81
+ return close_error(code, message)
82
+ return APIStatusError(
83
+ message, status_code=_ERROR_STATUS.get(code or "", 500), body=code or None
84
+ )
85
+
86
+
87
+ def close_error(code: int | None, reason: str) -> APIError:
88
+ """The error for a connection the server closed before the work was done."""
89
+ message = f"Chariot connection closed ({code}): {reason or 'no reason given'}"
90
+ if code == CLOSE_POLICY and reason.lower().startswith("unauthorized"):
91
+ return APIStatusError(message, status_code=401)
92
+ if code == CLOSE_NO_CREDITS:
93
+ return APIStatusError(message, status_code=402)
94
+ if code == CLOSE_BUSY:
95
+ return APIStatusError(message, status_code=429)
96
+ if code == CLOSE_POLICY and not any(r in reason.lower() for r in _TRANSIENT_POLICY_REASONS):
97
+ return APIStatusError(message, status_code=400)
98
+ return APIConnectionError(message)
99
+
100
+
101
+ async def http_error(resp: aiohttp.ClientResponse, request_id: str | None = None) -> APIStatusError:
102
+ """An error response from an HTTP endpoint, which carries `{"statusCode", "message"}`."""
103
+ text = await resp.text()
104
+ message = text
105
+ try:
106
+ body = json.loads(text)
107
+ if isinstance(body, dict) and body.get("message"):
108
+ message = str(body["message"])
109
+ except ValueError:
110
+ pass
111
+ return APIStatusError(
112
+ message or "Chariot API error",
113
+ status_code=resp.status,
114
+ request_id=request_id,
115
+ body=text or None,
116
+ )
@@ -0,0 +1,3 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger("livekit.plugins.chariot")
@@ -0,0 +1,19 @@
1
+ from typing import Literal
2
+
3
+ TTSModels = Literal["chariot-multilingual-v1", "chariot-v0"]
4
+
5
+ STTModels = Literal["chariot-1-asr-realtime"]
6
+
7
+ # Languages the speech-to-text model transcribes. Leave the language unset to detect it.
8
+ STTLanguages = Literal["en", "hi", "bn", "gu", "kn", "ml", "mr", "or", "pa", "sa", "ta", "te"]
9
+
10
+ # "balanced" favors latency. "high_accuracy" is more accurate (it writes numbers as digits,
11
+ # for example); its interim transcripts update less often.
12
+ STTModes = Literal["balanced", "high_accuracy"]
13
+
14
+ # "codemix" writes each word in the script it is usually written in, so English words in
15
+ # Hindi speech come out in Latin script. "native" writes everything in the language's script.
16
+ STTStyles = Literal["codemix", "native"]
17
+
18
+ # Mono 16-bit PCM rates the streaming API accepts.
19
+ STTSampleRates = Literal[8000, 16000, 22050, 24000, 32000, 44100, 48000]
File without changes