tavus-cli 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.
- tavus_cli-0.1.0.dist-info/METADATA +192 -0
- tavus_cli-0.1.0.dist-info/RECORD +32 -0
- tavus_cli-0.1.0.dist-info/WHEEL +4 -0
- tavus_cli-0.1.0.dist-info/entry_points.txt +3 -0
- tavus_mcp/__init__.py +4 -0
- tavus_mcp/cli/__init__.py +1 -0
- tavus_mcp/cli/main.py +1467 -0
- tavus_mcp/sdk/__init__.py +6 -0
- tavus_mcp/sdk/auth/__init__.py +1 -0
- tavus_mcp/sdk/auth/keyring_store.py +20 -0
- tavus_mcp/sdk/auth/oauth.py +131 -0
- tavus_mcp/sdk/auth/session.py +46 -0
- tavus_mcp/sdk/client/__init__.py +3 -0
- tavus_mcp/sdk/client/http.py +451 -0
- tavus_mcp/sdk/env.py +126 -0
- tavus_mcp/sdk/errors.py +46 -0
- tavus_mcp/sdk/patch.py +92 -0
- tavus_mcp/sdk/recipes/__init__.py +6 -0
- tavus_mcp/sdk/recipes/build_and_verify.py +618 -0
- tavus_mcp/sdk/recipes/options.py +67 -0
- tavus_mcp/sdk/recipes/quickstart.py +48 -0
- tavus_mcp/sdk/recipes/scaffold_embed.py +115 -0
- tavus_mcp/sdk/recipes/templates.py +58 -0
- tavus_mcp/sdk/recipes/tool_reuse.py +124 -0
- tavus_mcp/sdk/schemas/__init__.py +16 -0
- tavus_mcp/sdk/schemas/file_manifest.py +21 -0
- tavus_mcp/sdk/schemas/guardrail.py +84 -0
- tavus_mcp/sdk/schemas/objective.py +131 -0
- tavus_mcp/sdk/schemas/persona.py +174 -0
- tavus_mcp/sdk/schemas/pronunciation.py +73 -0
- tavus_mcp/sdk/schemas/tool.py +349 -0
- tavus_mcp/server.py +877 -0
tavus_mcp/server.py
ADDED
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from tavus_mcp.sdk.client.http import TavusClient
|
|
8
|
+
from tavus_mcp.sdk.recipes.build_and_verify import build_and_verify
|
|
9
|
+
from tavus_mcp.sdk.recipes.options import describe_persona_options
|
|
10
|
+
from tavus_mcp.sdk.recipes.quickstart import quickstart
|
|
11
|
+
from tavus_mcp.sdk.recipes.scaffold_embed import EmbedTarget, scaffold_embed
|
|
12
|
+
from tavus_mcp.sdk.recipes.templates import PersonaTemplate, persona_from_template
|
|
13
|
+
from tavus_mcp.sdk.recipes.tool_reuse import (
|
|
14
|
+
build_inline_deprecation_notice,
|
|
15
|
+
build_library_match_advisory,
|
|
16
|
+
collect_inline_tools,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
mcp = FastMCP(
|
|
20
|
+
"tavus",
|
|
21
|
+
instructions=(
|
|
22
|
+
"Persona tools are first-class objects, not inlined on the persona. "
|
|
23
|
+
"Before giving a persona a tool, check the saved library with "
|
|
24
|
+
"tavus_tool_list or tavus_describe_persona_options, then either ATTACH an "
|
|
25
|
+
"existing tool (tavus_persona_tools_attach) or CREATE a new one "
|
|
26
|
+
"(tavus_tool_create) and attach it. Writing tools inline via "
|
|
27
|
+
"tavus_patch_persona (layers.*.tools) is deprecated. Whenever you create a "
|
|
28
|
+
"new tool that overlaps an existing one, tell the user which existing tool "
|
|
29
|
+
"you saw and why its shape does not fit."
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _list_data(value: Any) -> list[Any]:
|
|
35
|
+
if isinstance(value, dict) and isinstance(value.get("data"), list):
|
|
36
|
+
return value["data"]
|
|
37
|
+
if isinstance(value, list):
|
|
38
|
+
return value
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@mcp.tool()
|
|
43
|
+
async def tavus_persona_list(
|
|
44
|
+
limit: int = 25,
|
|
45
|
+
page: int = 0,
|
|
46
|
+
persona_type: str | None = None,
|
|
47
|
+
) -> Any:
|
|
48
|
+
async with TavusClient.from_env() as client:
|
|
49
|
+
return await client.personas.list(limit=limit, page=page, persona_type=persona_type)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@mcp.tool()
|
|
53
|
+
async def tavus_persona_get(persona_id: str, include_settings: bool = False) -> Any:
|
|
54
|
+
async with TavusClient.from_env() as client:
|
|
55
|
+
return await client.personas.get(persona_id, include_settings=str(include_settings).lower())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@mcp.tool()
|
|
59
|
+
async def tavus_persona_create(
|
|
60
|
+
system_prompt: str | None = None,
|
|
61
|
+
persona_name: str = "Agentic Tavus Persona",
|
|
62
|
+
default_replica_id: str | None = None,
|
|
63
|
+
pipeline_mode: str = "full",
|
|
64
|
+
greeting: str | None = None,
|
|
65
|
+
context: str | None = None,
|
|
66
|
+
layers: dict[str, Any] | None = None,
|
|
67
|
+
memories: list[str] | None = None,
|
|
68
|
+
objectives_id: str | None = None,
|
|
69
|
+
guardrails_id: str | None = None,
|
|
70
|
+
guardrail_ids: list[str] | None = None,
|
|
71
|
+
guardrail_tags: list[str] | None = None,
|
|
72
|
+
document_ids: list[str] | None = None,
|
|
73
|
+
document_tags: list[str] | None = None,
|
|
74
|
+
is_template: bool | None = None,
|
|
75
|
+
) -> Any:
|
|
76
|
+
"""Create a persona. Beyond ``system_prompt``/``persona_name``, you can seed:
|
|
77
|
+
- ``pipeline_mode`` — ``"full"`` (default; LLM + TTS + STT + perception),
|
|
78
|
+
``"speech-to-speech"`` (no LLM), or ``"echo"`` (TTS + transport only).
|
|
79
|
+
``system_prompt`` is required for ``full`` and forbidden for the others.
|
|
80
|
+
- ``greeting`` — opening line the replica speaks.
|
|
81
|
+
- ``context`` — opaque text appended to the system prompt at runtime.
|
|
82
|
+
- ``layers`` — per-layer overrides (llm/tts/stt/perception/conversational_flow/
|
|
83
|
+
capabilities/knowledge_base). Same shape RQH stores; use
|
|
84
|
+
``tavus_describe_persona_options`` to see valid paths.
|
|
85
|
+
- ``memories`` — initial seed lines for the persona's memory store.
|
|
86
|
+
- ``objectives_id`` — attach an existing objective set.
|
|
87
|
+
- ``guardrails_id`` (legacy set) or ``guardrail_ids`` / ``guardrail_tags``
|
|
88
|
+
(flat per-rule shape) — attach guardrails.
|
|
89
|
+
- ``document_ids`` / ``document_tags`` — RAG knowledge base.
|
|
90
|
+
- ``is_template`` — flag this persona as a template others can clone.
|
|
91
|
+
|
|
92
|
+
To attach **tools**, create the persona first then call
|
|
93
|
+
``tavus_persona_tools_attach`` — tools live in a separate junction,
|
|
94
|
+
not at persona-create time."""
|
|
95
|
+
body: dict[str, Any] = {"persona_name": persona_name}
|
|
96
|
+
if pipeline_mode and pipeline_mode != "full":
|
|
97
|
+
body["pipeline_mode"] = pipeline_mode
|
|
98
|
+
if system_prompt is not None:
|
|
99
|
+
body["system_prompt"] = system_prompt
|
|
100
|
+
if default_replica_id:
|
|
101
|
+
body["default_replica_id"] = default_replica_id
|
|
102
|
+
if greeting is not None:
|
|
103
|
+
body["greeting"] = greeting
|
|
104
|
+
if context is not None:
|
|
105
|
+
body["context"] = context
|
|
106
|
+
if layers is not None:
|
|
107
|
+
body["layers"] = layers
|
|
108
|
+
if memories is not None:
|
|
109
|
+
body["memories"] = memories
|
|
110
|
+
if objectives_id:
|
|
111
|
+
body["objectives_id"] = objectives_id
|
|
112
|
+
if guardrails_id:
|
|
113
|
+
body["guardrails_id"] = guardrails_id
|
|
114
|
+
if guardrail_ids:
|
|
115
|
+
body["guardrail_ids"] = guardrail_ids
|
|
116
|
+
if guardrail_tags:
|
|
117
|
+
body["guardrail_tags"] = guardrail_tags
|
|
118
|
+
if document_ids:
|
|
119
|
+
body["document_ids"] = document_ids
|
|
120
|
+
if document_tags:
|
|
121
|
+
body["document_tags"] = document_tags
|
|
122
|
+
if is_template is not None:
|
|
123
|
+
body["is_template"] = is_template
|
|
124
|
+
async with TavusClient.from_env() as client:
|
|
125
|
+
return await client.personas.create(body)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@mcp.tool()
|
|
129
|
+
async def tavus_persona_delete(persona_id: str) -> Any:
|
|
130
|
+
async with TavusClient.from_env() as client:
|
|
131
|
+
return await client.personas.delete(persona_id)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@mcp.tool()
|
|
135
|
+
async def tavus_patch_persona(persona_id: str, ops: list[dict[str, Any]]) -> Any:
|
|
136
|
+
"""Patch a persona with JSON Patch operations.
|
|
137
|
+
|
|
138
|
+
Tools are first-class now: prefer tavus_tool_create + tavus_persona_tools_attach
|
|
139
|
+
over writing tools inline here. If any op writes inline tools (layers.*.tools),
|
|
140
|
+
the result carries `_inline_tools_deprecated` steering you to that flow."""
|
|
141
|
+
inline = collect_inline_tools(ops)
|
|
142
|
+
async with TavusClient.from_env() as client:
|
|
143
|
+
result = await client.personas.patch(persona_id, ops)
|
|
144
|
+
if inline and isinstance(result, dict):
|
|
145
|
+
result["_inline_tools_deprecated"] = build_inline_deprecation_notice(inline)
|
|
146
|
+
return result
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@mcp.tool()
|
|
150
|
+
async def tavus_describe_persona_options(persona_id: str) -> Any:
|
|
151
|
+
async with TavusClient.from_env() as client:
|
|
152
|
+
return await describe_persona_options(client, persona_id)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@mcp.tool()
|
|
156
|
+
async def tavus_replica_list(limit: int = 25, stock: bool = False) -> Any:
|
|
157
|
+
async with TavusClient.from_env() as client:
|
|
158
|
+
return await client.replicas.list(limit=limit, replica_type="system" if stock else None)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@mcp.tool()
|
|
162
|
+
async def tavus_conversation_create(
|
|
163
|
+
persona_id: str | None = None,
|
|
164
|
+
replica_id: str | None = None,
|
|
165
|
+
conversation_name: str | None = None,
|
|
166
|
+
) -> Any:
|
|
167
|
+
body = {
|
|
168
|
+
"persona_id": persona_id,
|
|
169
|
+
"replica_id": replica_id,
|
|
170
|
+
"conversation_name": conversation_name,
|
|
171
|
+
}
|
|
172
|
+
async with TavusClient.from_env() as client:
|
|
173
|
+
return await client.conversations.create({k: v for k, v in body.items() if v})
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@mcp.tool()
|
|
177
|
+
async def tavus_conversation_end(conversation_id: str) -> Any:
|
|
178
|
+
async with TavusClient.from_env() as client:
|
|
179
|
+
return await client.conversations.end(conversation_id)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@mcp.tool()
|
|
183
|
+
async def tavus_quickstart(
|
|
184
|
+
system_prompt: str,
|
|
185
|
+
persona_name: str = "Agentic Tavus Persona",
|
|
186
|
+
replica_id: str | None = None,
|
|
187
|
+
) -> Any:
|
|
188
|
+
async with TavusClient.from_env() as client:
|
|
189
|
+
return await quickstart(
|
|
190
|
+
client,
|
|
191
|
+
system_prompt=system_prompt,
|
|
192
|
+
persona_name=persona_name,
|
|
193
|
+
replica_id=replica_id,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@mcp.tool()
|
|
198
|
+
async def tavus_persona_from_template(
|
|
199
|
+
template: PersonaTemplate,
|
|
200
|
+
persona_name: str | None = None,
|
|
201
|
+
business_context: str | None = None,
|
|
202
|
+
default_replica_id: str | None = None,
|
|
203
|
+
layers: dict[str, Any] | None = None,
|
|
204
|
+
) -> Any:
|
|
205
|
+
async with TavusClient.from_env() as client:
|
|
206
|
+
return await persona_from_template(
|
|
207
|
+
client,
|
|
208
|
+
template=template,
|
|
209
|
+
persona_name=persona_name,
|
|
210
|
+
business_context=business_context,
|
|
211
|
+
default_replica_id=default_replica_id,
|
|
212
|
+
layers=layers,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@mcp.tool()
|
|
217
|
+
async def tavus_scaffold_embed(
|
|
218
|
+
conversation_url: str,
|
|
219
|
+
target: EmbedTarget = "iframe",
|
|
220
|
+
component_name: str = "TavusConversation",
|
|
221
|
+
) -> Any:
|
|
222
|
+
return scaffold_embed(
|
|
223
|
+
conversation_url=conversation_url,
|
|
224
|
+
target=target,
|
|
225
|
+
component_name=component_name,
|
|
226
|
+
).model_dump()
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@mcp.tool()
|
|
230
|
+
async def tavus_resource_list(resource: str, limit: int = 25) -> Any:
|
|
231
|
+
async with TavusClient.from_env() as client:
|
|
232
|
+
return await _resource(client, resource).list(limit=limit)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@mcp.tool()
|
|
236
|
+
async def tavus_guardrail_list(
|
|
237
|
+
limit: int = 25,
|
|
238
|
+
page: int = 1,
|
|
239
|
+
type: str = "user",
|
|
240
|
+
name_or_uuid: str | None = None,
|
|
241
|
+
tags: str | None = None,
|
|
242
|
+
legacy: bool = False,
|
|
243
|
+
verbose: bool = False,
|
|
244
|
+
) -> Any:
|
|
245
|
+
"""List guardrails. `type` is "user" (account), "system", or "all";
|
|
246
|
+
`legacy=False` returns the flat, per-rule shape used by the new
|
|
247
|
+
persona-builder UI; `verbose=True` adds `persona_refs` and `guardrail_type`."""
|
|
248
|
+
params: dict[str, Any] = {
|
|
249
|
+
"limit": limit,
|
|
250
|
+
"page": page,
|
|
251
|
+
"type": type,
|
|
252
|
+
"legacy": "true" if legacy else "false",
|
|
253
|
+
}
|
|
254
|
+
if name_or_uuid:
|
|
255
|
+
params["name_or_uuid"] = name_or_uuid
|
|
256
|
+
if tags:
|
|
257
|
+
params["tags"] = tags
|
|
258
|
+
if verbose:
|
|
259
|
+
params["verbose"] = "true"
|
|
260
|
+
async with TavusClient.from_env() as client:
|
|
261
|
+
return await client.guardrails.list(**params)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@mcp.tool()
|
|
265
|
+
async def tavus_guardrail_get(
|
|
266
|
+
guardrail_id: str, verbose: bool = False, legacy: bool | None = None
|
|
267
|
+
) -> Any:
|
|
268
|
+
"""Get a single guardrail. `verbose=True` adds `persona_refs` and, for
|
|
269
|
+
individual items, `guardrail_type`."""
|
|
270
|
+
params: dict[str, Any] = {}
|
|
271
|
+
if verbose:
|
|
272
|
+
params["verbose"] = "true"
|
|
273
|
+
if legacy is not None:
|
|
274
|
+
params["legacy"] = "true" if legacy else "false"
|
|
275
|
+
async with TavusClient.from_env() as client:
|
|
276
|
+
return await client.guardrails.get(guardrail_id, **params)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@mcp.tool()
|
|
280
|
+
async def tavus_guardrail_create(
|
|
281
|
+
guardrail_name: str,
|
|
282
|
+
guardrail_prompt: str,
|
|
283
|
+
modality: str = "verbal",
|
|
284
|
+
callback_url: str = "",
|
|
285
|
+
tool_call: dict[str, Any] | None = None,
|
|
286
|
+
app_message: bool = True,
|
|
287
|
+
tags: list[str] | None = None,
|
|
288
|
+
) -> Any:
|
|
289
|
+
"""Create a flat (non-set) guardrail. `modality` is "verbal" / "visual" /
|
|
290
|
+
"audio". `tool_call` is optional structured payload sent when the
|
|
291
|
+
guardrail fires; `app_message=True` emits a data-channel message."""
|
|
292
|
+
body: dict[str, Any] = {
|
|
293
|
+
"guardrail_name": guardrail_name,
|
|
294
|
+
"guardrail_prompt": guardrail_prompt,
|
|
295
|
+
"modality": modality,
|
|
296
|
+
"callback_url": callback_url,
|
|
297
|
+
"app_message": app_message,
|
|
298
|
+
"tags": tags or [],
|
|
299
|
+
}
|
|
300
|
+
if tool_call is not None:
|
|
301
|
+
body["tool_call"] = tool_call
|
|
302
|
+
async with TavusClient.from_env() as client:
|
|
303
|
+
return await client.guardrails.create(body)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@mcp.tool()
|
|
307
|
+
async def tavus_guardrail_patch(
|
|
308
|
+
guardrail_id: str,
|
|
309
|
+
guardrail_name: str | None = None,
|
|
310
|
+
guardrail_prompt: str | None = None,
|
|
311
|
+
modality: str | None = None,
|
|
312
|
+
callback_url: str | None = None,
|
|
313
|
+
tool_call: dict[str, Any] | None = None,
|
|
314
|
+
app_message: bool | None = None,
|
|
315
|
+
tags: list[str] | None = None,
|
|
316
|
+
) -> Any:
|
|
317
|
+
"""Update a guardrail. Omit fields to leave them unchanged. RQH replaces
|
|
318
|
+
the whole field where supplied (no JSON-Patch semantics)."""
|
|
319
|
+
body: dict[str, Any] = {}
|
|
320
|
+
if guardrail_name is not None:
|
|
321
|
+
body["guardrail_name"] = guardrail_name
|
|
322
|
+
if guardrail_prompt is not None:
|
|
323
|
+
body["guardrail_prompt"] = guardrail_prompt
|
|
324
|
+
if modality is not None:
|
|
325
|
+
body["modality"] = modality
|
|
326
|
+
if callback_url is not None:
|
|
327
|
+
body["callback_url"] = callback_url
|
|
328
|
+
if tool_call is not None:
|
|
329
|
+
body["tool_call"] = tool_call
|
|
330
|
+
if app_message is not None:
|
|
331
|
+
body["app_message"] = app_message
|
|
332
|
+
if tags is not None:
|
|
333
|
+
body["tags"] = tags
|
|
334
|
+
async with TavusClient.from_env() as client:
|
|
335
|
+
return await client.guardrails.patch(guardrail_id, body)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@mcp.tool()
|
|
339
|
+
async def tavus_guardrail_delete(guardrail_id: str) -> Any:
|
|
340
|
+
async with TavusClient.from_env() as client:
|
|
341
|
+
return await client.guardrails.delete(guardrail_id)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@mcp.tool()
|
|
345
|
+
async def tavus_guardrail_tags(
|
|
346
|
+
search: str | None = None,
|
|
347
|
+
page: int | None = None,
|
|
348
|
+
limit: int | None = None,
|
|
349
|
+
) -> Any:
|
|
350
|
+
"""List tags applied to the account's guardrails. Mirrors the documents
|
|
351
|
+
`/tags` endpoint shape (`{tags, total_count}`)."""
|
|
352
|
+
async with TavusClient.from_env() as client:
|
|
353
|
+
return await client.guardrails.tags(search=search, page=page, limit=limit)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@mcp.tool()
|
|
357
|
+
async def tavus_objective_list(
|
|
358
|
+
limit: int = 25,
|
|
359
|
+
page: int = 1,
|
|
360
|
+
type: str = "user",
|
|
361
|
+
name_or_uuid: str | None = None,
|
|
362
|
+
sort: str = "ascending",
|
|
363
|
+
) -> Any:
|
|
364
|
+
"""List objective *sets*. Objectives still bundle into named sets that
|
|
365
|
+
personas reference by ``objectives_id`` — unlike guardrails, sets were
|
|
366
|
+
not flattened."""
|
|
367
|
+
params: dict[str, Any] = {
|
|
368
|
+
"limit": limit,
|
|
369
|
+
"page": page,
|
|
370
|
+
"type": type,
|
|
371
|
+
"sort": sort,
|
|
372
|
+
}
|
|
373
|
+
if name_or_uuid:
|
|
374
|
+
params["name_or_uuid"] = name_or_uuid
|
|
375
|
+
async with TavusClient.from_env() as client:
|
|
376
|
+
return await client.objectives.list(**params)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
@mcp.tool()
|
|
380
|
+
async def tavus_objective_get(objectives_id: str) -> Any:
|
|
381
|
+
async with TavusClient.from_env() as client:
|
|
382
|
+
return await client.objectives.get(objectives_id)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@mcp.tool()
|
|
386
|
+
async def tavus_objective_create(
|
|
387
|
+
data: list[dict[str, Any]],
|
|
388
|
+
name: str = "",
|
|
389
|
+
allow_loops: bool = False,
|
|
390
|
+
) -> Any:
|
|
391
|
+
"""Create an objective set. ``data`` is a list of objective items —
|
|
392
|
+
each carries ``objective_name`` (letters/digits/underscores only),
|
|
393
|
+
``objective_prompt``, optional ``confirmation_mode`` ("auto" | "manual"),
|
|
394
|
+
``modality`` ("verbal" | "visual" | "audio"), ``output_variables``,
|
|
395
|
+
``callback_url``, ``tool_call``, and chains either via
|
|
396
|
+
``next_required_objective`` (linear) **or** ``next_conditional_objectives``
|
|
397
|
+
(branching) — not both. Exactly one item must be the root (not
|
|
398
|
+
referenced by any other) unless ``allow_loops=true``."""
|
|
399
|
+
body = {"name": name, "data": data, "allow_loops": allow_loops}
|
|
400
|
+
async with TavusClient.from_env() as client:
|
|
401
|
+
return await client.objectives.create(body)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@mcp.tool()
|
|
405
|
+
async def tavus_objective_patch(objectives_id: str, ops: list[dict[str, Any]]) -> Any:
|
|
406
|
+
"""Patch an objective set with JSON Patch ops. RQH applies them to the
|
|
407
|
+
set document and re-runs cycle/single-root validation on the result.
|
|
408
|
+
Common ops:
|
|
409
|
+
- ``{"op": "replace", "path": "/data/0/objective_prompt", "value": "..."}``
|
|
410
|
+
- ``{"op": "add", "path": "/data/-", "value": {<new objective>}}``
|
|
411
|
+
- ``{"op": "remove", "path": "/data/2"}``"""
|
|
412
|
+
async with TavusClient.from_env() as client:
|
|
413
|
+
return await client.objectives.patch(objectives_id, ops)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@mcp.tool()
|
|
417
|
+
async def tavus_objective_delete(objectives_id: str) -> Any:
|
|
418
|
+
async with TavusClient.from_env() as client:
|
|
419
|
+
return await client.objectives.delete(objectives_id)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@mcp.tool()
|
|
423
|
+
async def tavus_objective_validate(
|
|
424
|
+
data: list[dict[str, Any]],
|
|
425
|
+
name: str = "",
|
|
426
|
+
allow_loops: bool = False,
|
|
427
|
+
) -> Any:
|
|
428
|
+
"""Validate an objective-set payload (cycles, single root, references)
|
|
429
|
+
without persisting. Useful before bulk-importing or after a manual edit."""
|
|
430
|
+
body = {"name": name, "data": data, "allow_loops": allow_loops}
|
|
431
|
+
async with TavusClient.from_env() as client:
|
|
432
|
+
return await client.objectives.validate(body)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@mcp.tool()
|
|
436
|
+
async def tavus_tool_list(
|
|
437
|
+
limit: int = 25,
|
|
438
|
+
page: int = 1,
|
|
439
|
+
type: str = "user",
|
|
440
|
+
name_or_uuid: str | None = None,
|
|
441
|
+
sort: str = "ascending",
|
|
442
|
+
) -> Any:
|
|
443
|
+
"""List tools. `type` is "user" / "system" / "all"."""
|
|
444
|
+
params: dict[str, Any] = {
|
|
445
|
+
"limit": limit,
|
|
446
|
+
"page": page,
|
|
447
|
+
"type": type,
|
|
448
|
+
"sort": sort,
|
|
449
|
+
}
|
|
450
|
+
if name_or_uuid:
|
|
451
|
+
params["name_or_uuid"] = name_or_uuid
|
|
452
|
+
async with TavusClient.from_env() as client:
|
|
453
|
+
return await client.tools.list(**params)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
@mcp.tool()
|
|
457
|
+
async def tavus_tool_get(tool_id: str) -> Any:
|
|
458
|
+
async with TavusClient.from_env() as client:
|
|
459
|
+
return await client.tools.get(tool_id)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
@mcp.tool()
|
|
463
|
+
async def tavus_tool_create(
|
|
464
|
+
name: str,
|
|
465
|
+
description: str,
|
|
466
|
+
parameters: dict[str, Any] | None = None,
|
|
467
|
+
delivery: dict[str, Any] | None = None,
|
|
468
|
+
origin: str = "llm",
|
|
469
|
+
on_call: str | None = None,
|
|
470
|
+
on_resolve: str | None = "fire_and_forget",
|
|
471
|
+
static_filler: str | None = None,
|
|
472
|
+
tags: list[str] | None = None,
|
|
473
|
+
) -> Any:
|
|
474
|
+
"""Create a tool.
|
|
475
|
+
|
|
476
|
+
`delivery` picks the channel: ``{"app_message": true}`` (Daily data
|
|
477
|
+
channel) or ``{"api": {"url": "https://...", "method": "POST", ...}}``
|
|
478
|
+
for direct third-party HTTPS calls. The HTTP form supports
|
|
479
|
+
``headers``, ``query_params``, ``body_template``, ``content_type``,
|
|
480
|
+
``timeout`` (default 10s, max 60), and ``auth`` with type ``none |
|
|
481
|
+
bearer | basic | api_key | hmac | oauth2_client_credentials``.
|
|
482
|
+
Placeholders ``{ident}`` in url/body/query must be declared in
|
|
483
|
+
``parameters.properties``. Description + parameters JSON must total
|
|
484
|
+
≤ 10,000 chars."""
|
|
485
|
+
body: dict[str, Any] = {
|
|
486
|
+
"name": name,
|
|
487
|
+
"description": description,
|
|
488
|
+
"parameters": parameters or {},
|
|
489
|
+
"delivery": delivery or {"app_message": True},
|
|
490
|
+
"origin": origin,
|
|
491
|
+
}
|
|
492
|
+
if on_call is not None:
|
|
493
|
+
body["on_call"] = on_call
|
|
494
|
+
if on_resolve is not None:
|
|
495
|
+
body["on_resolve"] = on_resolve
|
|
496
|
+
if static_filler is not None:
|
|
497
|
+
body["static_filler"] = static_filler
|
|
498
|
+
if tags is not None:
|
|
499
|
+
body["tags"] = tags
|
|
500
|
+
async with TavusClient.from_env() as client:
|
|
501
|
+
created = await client.tools.create(body)
|
|
502
|
+
# Surface same-origin saved tools so the agent reuses instead of
|
|
503
|
+
# duplicating. Injected as a namespaced key on the returned tool.
|
|
504
|
+
if isinstance(created, dict):
|
|
505
|
+
saved = _list_data(await client.tools.list(limit=100))
|
|
506
|
+
advisory = build_library_match_advisory(origin, name, saved)
|
|
507
|
+
if advisory:
|
|
508
|
+
created["_tool_reuse_advisory"] = advisory
|
|
509
|
+
return created
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
@mcp.tool()
|
|
513
|
+
async def tavus_tool_patch(
|
|
514
|
+
tool_id: str,
|
|
515
|
+
name: str | None = None,
|
|
516
|
+
description: str | None = None,
|
|
517
|
+
parameters: dict[str, Any] | None = None,
|
|
518
|
+
delivery: dict[str, Any] | None = None,
|
|
519
|
+
origin: str | None = None,
|
|
520
|
+
on_call: str | None = None,
|
|
521
|
+
on_resolve: str | None = None,
|
|
522
|
+
static_filler: str | None = None,
|
|
523
|
+
tags: list[str] | None = None,
|
|
524
|
+
) -> Any:
|
|
525
|
+
"""Update a tool. Omit fields to leave them unchanged. Secrets that came
|
|
526
|
+
back scrubbed (``********``) from a prior GET must be omitted, not
|
|
527
|
+
re-sent — RQH rejects the placeholder."""
|
|
528
|
+
body: dict[str, Any] = {}
|
|
529
|
+
if name is not None:
|
|
530
|
+
body["name"] = name
|
|
531
|
+
if description is not None:
|
|
532
|
+
body["description"] = description
|
|
533
|
+
if parameters is not None:
|
|
534
|
+
body["parameters"] = parameters
|
|
535
|
+
if delivery is not None:
|
|
536
|
+
body["delivery"] = delivery
|
|
537
|
+
if origin is not None:
|
|
538
|
+
body["origin"] = origin
|
|
539
|
+
if on_call is not None:
|
|
540
|
+
body["on_call"] = on_call
|
|
541
|
+
if on_resolve is not None:
|
|
542
|
+
body["on_resolve"] = on_resolve
|
|
543
|
+
if static_filler is not None:
|
|
544
|
+
body["static_filler"] = static_filler
|
|
545
|
+
if tags is not None:
|
|
546
|
+
body["tags"] = tags
|
|
547
|
+
async with TavusClient.from_env() as client:
|
|
548
|
+
return await client.tools.patch(tool_id, body)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
@mcp.tool()
|
|
552
|
+
async def tavus_tool_delete(tool_id: str) -> Any:
|
|
553
|
+
async with TavusClient.from_env() as client:
|
|
554
|
+
return await client.tools.delete(tool_id)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@mcp.tool()
|
|
558
|
+
async def tavus_pronunciation_dictionary_list(
|
|
559
|
+
limit: int = 25,
|
|
560
|
+
page: int = 0,
|
|
561
|
+
sort: str = "desc",
|
|
562
|
+
) -> Any:
|
|
563
|
+
"""List pronunciation dictionaries — referenced from personas via
|
|
564
|
+
``layers.tts.pronunciation_dictionary_id``."""
|
|
565
|
+
async with TavusClient.from_env() as client:
|
|
566
|
+
return await client.pronunciation_dictionaries.list(limit=limit, page=page, sort=sort)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
@mcp.tool()
|
|
570
|
+
async def tavus_pronunciation_dictionary_get(dictionary_id: str) -> Any:
|
|
571
|
+
async with TavusClient.from_env() as client:
|
|
572
|
+
return await client.pronunciation_dictionaries.get(dictionary_id)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
@mcp.tool()
|
|
576
|
+
async def tavus_pronunciation_dictionary_create(
|
|
577
|
+
name: str, rules: list[dict[str, Any]] | None = None
|
|
578
|
+
) -> Any:
|
|
579
|
+
"""Create a pronunciation dictionary. Each rule is
|
|
580
|
+
``{text, pronunciation, type: "alias" | "ipa", alphabet?, case_sensitive?,
|
|
581
|
+
word_boundaries?}``. ``alias`` swaps the literal text for the
|
|
582
|
+
pronunciation string before TTS; ``ipa`` interprets the pronunciation
|
|
583
|
+
as IPA via SSML phoneme tags. ``text`` values must be unique within
|
|
584
|
+
the dictionary; max 10,000 rules per dictionary."""
|
|
585
|
+
body = {"name": name, "rules": rules or []}
|
|
586
|
+
async with TavusClient.from_env() as client:
|
|
587
|
+
return await client.pronunciation_dictionaries.create(body)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
@mcp.tool()
|
|
591
|
+
async def tavus_pronunciation_dictionary_patch(
|
|
592
|
+
dictionary_id: str,
|
|
593
|
+
name: str | None = None,
|
|
594
|
+
rules: list[dict[str, Any]] | None = None,
|
|
595
|
+
) -> Any:
|
|
596
|
+
"""Patch a pronunciation dictionary. Supplying ``rules`` replaces the
|
|
597
|
+
full list (RQH does not merge rule arrays)."""
|
|
598
|
+
body: dict[str, Any] = {}
|
|
599
|
+
if name is not None:
|
|
600
|
+
body["name"] = name
|
|
601
|
+
if rules is not None:
|
|
602
|
+
body["rules"] = rules
|
|
603
|
+
async with TavusClient.from_env() as client:
|
|
604
|
+
return await client.pronunciation_dictionaries.patch(dictionary_id, body)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
@mcp.tool()
|
|
608
|
+
async def tavus_pronunciation_dictionary_delete(dictionary_id: str) -> Any:
|
|
609
|
+
async with TavusClient.from_env() as client:
|
|
610
|
+
return await client.pronunciation_dictionaries.delete(dictionary_id)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
@mcp.tool()
|
|
614
|
+
async def tavus_persona_tools_list(persona_id: str) -> Any:
|
|
615
|
+
"""List the tools currently attached to a persona."""
|
|
616
|
+
async with TavusClient.from_env() as client:
|
|
617
|
+
return await client.personas.list_tools(persona_id)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
@mcp.tool()
|
|
621
|
+
async def tavus_persona_tools_attach(persona_id: str, tool_ids: list[str]) -> Any:
|
|
622
|
+
"""Attach one or more existing tools to a persona by ID. A persona can
|
|
623
|
+
hold up to 50 tools.
|
|
624
|
+
|
|
625
|
+
Vision/audio tools only fire when the persona runs Raven, so if any
|
|
626
|
+
attached tool is vision/audio this bumps the persona's perception_model to
|
|
627
|
+
raven-1 (response carries `_perception_model_bumped_to_raven_1`)."""
|
|
628
|
+
async with TavusClient.from_env() as client:
|
|
629
|
+
attached = await client.personas.attach_tools(persona_id, tool_ids)
|
|
630
|
+
tools_data = _list_data(attached)
|
|
631
|
+
origins = {t.get("origin") for t in tools_data if isinstance(t, dict)}
|
|
632
|
+
perception_bumped = False
|
|
633
|
+
if origins & {"vision", "audio"}:
|
|
634
|
+
persona = await client.personas.get(persona_id, include_settings="true")
|
|
635
|
+
layers = persona.get("layers") or {} if isinstance(persona, dict) else {}
|
|
636
|
+
perception = layers.get("perception")
|
|
637
|
+
if not isinstance(perception, dict):
|
|
638
|
+
op = {
|
|
639
|
+
"op": "add",
|
|
640
|
+
"path": "/layers/perception",
|
|
641
|
+
"value": {"perception_model": "raven-1"},
|
|
642
|
+
}
|
|
643
|
+
needs_bump = True
|
|
644
|
+
elif perception.get("perception_model") not in {"raven-0", "raven-1"}:
|
|
645
|
+
op = {
|
|
646
|
+
"op": "add",
|
|
647
|
+
"path": "/layers/perception/perception_model",
|
|
648
|
+
"value": "raven-1",
|
|
649
|
+
}
|
|
650
|
+
needs_bump = True
|
|
651
|
+
else:
|
|
652
|
+
needs_bump = False
|
|
653
|
+
if needs_bump:
|
|
654
|
+
await client.personas.patch(persona_id, [op])
|
|
655
|
+
perception_bumped = True
|
|
656
|
+
if isinstance(attached, dict):
|
|
657
|
+
attached["_perception_model_bumped_to_raven_1"] = perception_bumped
|
|
658
|
+
return attached
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
@mcp.tool()
|
|
662
|
+
async def tavus_persona_tools_detach(persona_id: str, tool_id: str) -> Any:
|
|
663
|
+
"""Detach a tool from a persona."""
|
|
664
|
+
async with TavusClient.from_env() as client:
|
|
665
|
+
return await client.personas.detach_tool(persona_id, tool_id)
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
@mcp.tool()
|
|
669
|
+
async def tavus_builder_create(
|
|
670
|
+
name: str,
|
|
671
|
+
greeting: str | None = None,
|
|
672
|
+
persona_id: str | None = None,
|
|
673
|
+
model: str | None = None,
|
|
674
|
+
) -> Any:
|
|
675
|
+
body: dict[str, Any] = {"name": name}
|
|
676
|
+
if greeting is not None:
|
|
677
|
+
body["greeting"] = greeting
|
|
678
|
+
if persona_id:
|
|
679
|
+
body["persona_id"] = persona_id
|
|
680
|
+
if model:
|
|
681
|
+
body["model"] = model
|
|
682
|
+
async with TavusClient.from_env() as client:
|
|
683
|
+
return await client.builders.create(body)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
@mcp.tool()
|
|
687
|
+
async def tavus_builder_list(
|
|
688
|
+
limit: int | None = None,
|
|
689
|
+
page: int | None = None,
|
|
690
|
+
persona_id: str | None = None,
|
|
691
|
+
name: str | None = None,
|
|
692
|
+
status: str | None = None,
|
|
693
|
+
) -> Any:
|
|
694
|
+
async with TavusClient.from_env() as client:
|
|
695
|
+
return await client.builders.list(
|
|
696
|
+
limit=limit, page=page, persona_id=persona_id, name=name, status=status
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
@mcp.tool()
|
|
701
|
+
async def tavus_builder_get(builder_id: str) -> Any:
|
|
702
|
+
async with TavusClient.from_env() as client:
|
|
703
|
+
return await client.builders.get(builder_id)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
@mcp.tool()
|
|
707
|
+
async def tavus_builder_delete(builder_id: str) -> Any:
|
|
708
|
+
async with TavusClient.from_env() as client:
|
|
709
|
+
return await client.builders.delete(builder_id)
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
@mcp.tool()
|
|
713
|
+
async def tavus_builder_chat(builder_id: str, message: str) -> Any:
|
|
714
|
+
"""Send a chat turn to the builder. Returns a structured response with
|
|
715
|
+
the assistant text, autocomplete suggestions, a `draft_ready` flag, and
|
|
716
|
+
the list of persona sections the builder LLM signaled as targets for
|
|
717
|
+
follow-up scoped updates (use ``tavus_builder_update_*`` to apply)."""
|
|
718
|
+
async with TavusClient.from_env() as client:
|
|
719
|
+
return await client.builders.chat(builder_id, message)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
@mcp.tool()
|
|
723
|
+
async def tavus_builder_chat_history(builder_id: str, limit: int = 50) -> Any:
|
|
724
|
+
async with TavusClient.from_env() as client:
|
|
725
|
+
return await client.builders.chat_history(builder_id, limit=limit)
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
@mcp.tool()
|
|
729
|
+
async def tavus_builder_append_messages(builder_id: str, messages: list[dict[str, str]]) -> Any:
|
|
730
|
+
async with TavusClient.from_env() as client:
|
|
731
|
+
return await client.builders.append_messages(builder_id, messages)
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
@mcp.tool()
|
|
735
|
+
async def tavus_builder_update_objectives(builder_id: str, message: str) -> Any:
|
|
736
|
+
async with TavusClient.from_env() as client:
|
|
737
|
+
return await client.builders.update_objectives(builder_id, message)
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
@mcp.tool()
|
|
741
|
+
async def tavus_builder_update_guardrails(builder_id: str, message: str) -> Any:
|
|
742
|
+
async with TavusClient.from_env() as client:
|
|
743
|
+
return await client.builders.update_guardrails(builder_id, message)
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
@mcp.tool()
|
|
747
|
+
async def tavus_builder_update_greeting(builder_id: str, message: str) -> Any:
|
|
748
|
+
async with TavusClient.from_env() as client:
|
|
749
|
+
return await client.builders.update_greeting(builder_id, message)
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
@mcp.tool()
|
|
753
|
+
async def tavus_builder_update_personality(
|
|
754
|
+
builder_id: str,
|
|
755
|
+
message: str,
|
|
756
|
+
persona_name: bool = False,
|
|
757
|
+
system_prompt: bool = False,
|
|
758
|
+
) -> Any:
|
|
759
|
+
async with TavusClient.from_env() as client:
|
|
760
|
+
return await client.builders.update_personality(
|
|
761
|
+
builder_id,
|
|
762
|
+
message,
|
|
763
|
+
persona_name=persona_name,
|
|
764
|
+
system_prompt=system_prompt,
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
@mcp.tool()
|
|
769
|
+
async def tavus_builder_publish(builder_id: str) -> Any:
|
|
770
|
+
async with TavusClient.from_env() as client:
|
|
771
|
+
return await client.builders.publish(builder_id)
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
@mcp.tool()
|
|
775
|
+
async def tavus_chat_start(
|
|
776
|
+
persona_id: str,
|
|
777
|
+
custom_greeting: str | None = None,
|
|
778
|
+
conversation_name: str | None = None,
|
|
779
|
+
) -> Any:
|
|
780
|
+
"""Start a text-only (chat-mode) conversation with a persona. Use this
|
|
781
|
+
to test a built persona by sending typed turns and reading replies; no
|
|
782
|
+
replica video is rendered."""
|
|
783
|
+
body: dict[str, Any] = {"persona_id": persona_id, "chat": True}
|
|
784
|
+
if custom_greeting:
|
|
785
|
+
body["custom_greeting"] = custom_greeting
|
|
786
|
+
if conversation_name:
|
|
787
|
+
body["conversation_name"] = conversation_name
|
|
788
|
+
async with TavusClient.from_env() as client:
|
|
789
|
+
return await client.conversations.create(body)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
@mcp.tool()
|
|
793
|
+
async def tavus_chat_turn(
|
|
794
|
+
conversation_id: str,
|
|
795
|
+
text: str,
|
|
796
|
+
timeout_s: float = 20.0,
|
|
797
|
+
) -> Any:
|
|
798
|
+
"""Send one user turn to a chat-mode conversation and wait for the
|
|
799
|
+
persona's reply. Returns ``{text: <reply>}``."""
|
|
800
|
+
async with TavusClient.from_env() as client:
|
|
801
|
+
reply = await client.conversations.chat_turn(conversation_id, text, timeout_s=timeout_s)
|
|
802
|
+
return {"text": reply}
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
@mcp.tool()
|
|
806
|
+
async def tavus_chat_end(conversation_id: str) -> Any:
|
|
807
|
+
"""End a chat-mode conversation. Same call as ``tavus_conversation_end``."""
|
|
808
|
+
async with TavusClient.from_env() as client:
|
|
809
|
+
return await client.conversations.end(conversation_id)
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
@mcp.tool()
|
|
813
|
+
async def tavus_persona_preview(
|
|
814
|
+
persona_id: str,
|
|
815
|
+
replica_id: str | None = None,
|
|
816
|
+
conversation_name: str | None = None,
|
|
817
|
+
) -> Any:
|
|
818
|
+
"""Start a full (audio/video) preview conversation against a persona and
|
|
819
|
+
return ``{conversation_id, conversation_url, ...}``. Hand the URL to a
|
|
820
|
+
human when you want them to visually verify the persona's behavior."""
|
|
821
|
+
body: dict[str, Any] = {"persona_id": persona_id}
|
|
822
|
+
if replica_id:
|
|
823
|
+
body["replica_id"] = replica_id
|
|
824
|
+
if conversation_name:
|
|
825
|
+
body["conversation_name"] = conversation_name
|
|
826
|
+
async with TavusClient.from_env() as client:
|
|
827
|
+
if not replica_id:
|
|
828
|
+
persona = await client.personas.get(persona_id)
|
|
829
|
+
default_replica = (
|
|
830
|
+
persona.get("default_replica_id") if isinstance(persona, dict) else None
|
|
831
|
+
)
|
|
832
|
+
if default_replica:
|
|
833
|
+
body["replica_id"] = default_replica
|
|
834
|
+
return await client.conversations.create(body)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
@mcp.tool()
|
|
838
|
+
async def tavus_persona_build_and_verify(
|
|
839
|
+
prompt: str,
|
|
840
|
+
replica_id: str | None = None,
|
|
841
|
+
max_rounds: int = 4,
|
|
842
|
+
answers: list[str] | None = None,
|
|
843
|
+
) -> Any:
|
|
844
|
+
"""Build a persona from one creator prompt, optionally using supplied
|
|
845
|
+
creator answers for the builder's follow-up questions, then publish and
|
|
846
|
+
validate it through CVI chat mode. When ``replica_id`` is omitted, RQH
|
|
847
|
+
selects and attaches the default replica. Validation probes are generated
|
|
848
|
+
from the resulting persona spec."""
|
|
849
|
+
async with TavusClient.from_env() as client:
|
|
850
|
+
return await build_and_verify(
|
|
851
|
+
client,
|
|
852
|
+
prompt=prompt,
|
|
853
|
+
replica_id=replica_id,
|
|
854
|
+
max_rounds=max_rounds,
|
|
855
|
+
answers=answers,
|
|
856
|
+
auto_refine=True,
|
|
857
|
+
max_refine_rounds=1,
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def _resource(client: TavusClient, resource: str) -> Any:
|
|
862
|
+
normalized = resource.strip().lower().replace("-", "_")
|
|
863
|
+
aliases = {
|
|
864
|
+
"guardrail": "guardrails",
|
|
865
|
+
"objective": "objectives",
|
|
866
|
+
"document": "documents",
|
|
867
|
+
"voice": "voices",
|
|
868
|
+
"tool": "tools",
|
|
869
|
+
}
|
|
870
|
+
attr = aliases.get(normalized, normalized)
|
|
871
|
+
if attr not in {"guardrails", "objectives", "documents", "voices", "tools"}:
|
|
872
|
+
raise ValueError("resource must be guardrails, objectives, documents, voices, or tools")
|
|
873
|
+
return getattr(client, attr)
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def main() -> None:
|
|
877
|
+
mcp.run()
|