Scripts-deploy-VMs-models
#1
by RCaz - opened
crew2.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import concurrent.futures
|
| 2 |
import sys
|
| 3 |
import uuid
|
| 4 |
-
from contextlib import contextmanager
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
|
| 7 |
from crewai import Agent, Crew, LLM, Process, Task
|
|
@@ -14,15 +14,19 @@ load_dotenv()
|
|
| 14 |
try:
|
| 15 |
from openinference.instrumentation import using_session as _using_session
|
| 16 |
except ImportError:
|
|
|
|
| 17 |
@contextmanager
|
| 18 |
def _using_session(session_id): # noqa: F811
|
| 19 |
yield
|
| 20 |
|
|
|
|
| 21 |
try:
|
| 22 |
from openinference.semconv.trace import SpanAttributes
|
| 23 |
except ImportError:
|
|
|
|
| 24 |
class _SpanAttributes:
|
| 25 |
SESSION_ID = "session_id"
|
|
|
|
| 26 |
SpanAttributes = _SpanAttributes() # type: ignore
|
| 27 |
|
| 28 |
# ------ Phoenix / OpenTelemetry tracing ------
|
|
@@ -46,9 +50,11 @@ try:
|
|
| 46 |
endpoint="https://RCaz-phoenix-arize-observability.hf.space/v1/traces",
|
| 47 |
headers=build_hf_headers(),
|
| 48 |
)
|
|
|
|
| 49 |
CrewAIInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
|
|
| 50 |
except ImportError:
|
| 51 |
-
|
| 52 |
|
| 53 |
|
| 54 |
###### The agentic app
|
|
@@ -232,7 +238,7 @@ Two research crews investigated it. Here are their findings:
|
|
| 232 |
{result_opposite}
|
| 233 |
|
| 234 |
Your job:
|
| 235 |
-
Create a highly detailed visual scene description that captures the dialogue, contrast, or tension between these two perspectives. Describe the composition, colors, lighting, mood, subjects, setting, and visual metaphor in vivid detail — as if instructing an artist or image generation model.
|
| 236 |
|
| 237 |
Output exactly in this format:
|
| 238 |
|
|
@@ -281,21 +287,26 @@ def generate_image(prompt: str) -> bytes:
|
|
| 281 |
|
| 282 |
import httpx as _httpx
|
| 283 |
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
|
|
|
|
|
|
|
|
|
| 293 |
|
| 294 |
|
| 295 |
# ------ Caption Generation (direct LLM call with image prompt context) ------
|
| 296 |
|
| 297 |
|
| 298 |
-
def generate_caption(
|
|
|
|
|
|
|
| 299 |
"""
|
| 300 |
Send the research + image prompt to Gemma 4 and get a 30-second
|
| 301 |
spoken caption + voice style back.
|
|
@@ -343,15 +354,34 @@ VOICE_STYLE: <voice style description>""",
|
|
| 343 |
}
|
| 344 |
|
| 345 |
with _using_session(session_id):
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
timeout=300,
|
| 351 |
)
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
print(f"Gemma response:\n{text}\n")
|
| 357 |
|
|
@@ -369,7 +399,9 @@ VOICE_STYLE: <voice style description>""",
|
|
| 369 |
# ------ Voice Generation (VoxCPM on Modal) ------
|
| 370 |
|
| 371 |
|
| 372 |
-
def generate_voice(
|
|
|
|
|
|
|
| 373 |
"""Send script to VoxCPM on Modal T4, return WAV bytes."""
|
| 374 |
if session_id is None:
|
| 375 |
session_id = str(uuid.uuid4())
|
|
@@ -381,15 +413,20 @@ def generate_voice(voice_style: str, voice_script: str, session_id: str | None =
|
|
| 381 |
import httpx as _httpx
|
| 382 |
|
| 383 |
with _using_session(session_id):
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
|
| 394 |
|
| 395 |
# ------ Audio Transcription (Cohere Transcribe on Modal) ------
|
|
@@ -405,14 +442,19 @@ def transcribe_audio(audio_path: str, session_id: str | None = None) -> str:
|
|
| 405 |
url = _get_transcribe_url()
|
| 406 |
|
| 407 |
with _using_session(session_id):
|
| 408 |
-
with
|
| 409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
|
| 417 |
|
| 418 |
# ------ CLI Entry Point ------
|
|
@@ -461,7 +503,9 @@ if __name__ == "__main__":
|
|
| 461 |
caption_result = None
|
| 462 |
if prompt and corroborate and opposite:
|
| 463 |
try:
|
| 464 |
-
caption_result = generate_caption(
|
|
|
|
|
|
|
| 465 |
if caption_result["caption"]:
|
| 466 |
print(f"\n✓ Caption: {caption_result['caption']}")
|
| 467 |
if caption_result["voice_style"]:
|
|
@@ -472,10 +516,16 @@ if __name__ == "__main__":
|
|
| 472 |
print("(Skipping caption generation — missing prompt or research)")
|
| 473 |
|
| 474 |
# Generate voice
|
| 475 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
| 476 |
try:
|
| 477 |
audio_bytes = generate_voice(
|
| 478 |
-
caption_result["voice_style"],
|
|
|
|
|
|
|
| 479 |
)
|
| 480 |
filename = "crew_voice_output.wav"
|
| 481 |
with open(filename, "wb") as f:
|
|
|
|
| 1 |
import concurrent.futures
|
| 2 |
import sys
|
| 3 |
import uuid
|
| 4 |
+
from contextlib import contextmanager, nullcontext
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
|
| 7 |
from crewai import Agent, Crew, LLM, Process, Task
|
|
|
|
| 14 |
try:
|
| 15 |
from openinference.instrumentation import using_session as _using_session
|
| 16 |
except ImportError:
|
| 17 |
+
|
| 18 |
@contextmanager
|
| 19 |
def _using_session(session_id): # noqa: F811
|
| 20 |
yield
|
| 21 |
|
| 22 |
+
|
| 23 |
try:
|
| 24 |
from openinference.semconv.trace import SpanAttributes
|
| 25 |
except ImportError:
|
| 26 |
+
|
| 27 |
class _SpanAttributes:
|
| 28 |
SESSION_ID = "session_id"
|
| 29 |
+
|
| 30 |
SpanAttributes = _SpanAttributes() # type: ignore
|
| 31 |
|
| 32 |
# ------ Phoenix / OpenTelemetry tracing ------
|
|
|
|
| 50 |
endpoint="https://RCaz-phoenix-arize-observability.hf.space/v1/traces",
|
| 51 |
headers=build_hf_headers(),
|
| 52 |
)
|
| 53 |
+
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
|
| 54 |
CrewAIInstrumentor().instrument(tracer_provider=tracer_provider)
|
| 55 |
+
tracer = trace.get_tracer("crewai")
|
| 56 |
except ImportError:
|
| 57 |
+
tracer = None
|
| 58 |
|
| 59 |
|
| 60 |
###### The agentic app
|
|
|
|
| 238 |
{result_opposite}
|
| 239 |
|
| 240 |
Your job:
|
| 241 |
+
Create a highly detailed visual scene description that captures and merges the dialogue, contrast, or tension between these two perspectives. Describe the composition, colors, lighting, mood, subjects, setting, and visual metaphor in vivid detail — as if instructing an artist or image generation model.
|
| 242 |
|
| 243 |
Output exactly in this format:
|
| 244 |
|
|
|
|
| 287 |
|
| 288 |
import httpx as _httpx
|
| 289 |
|
| 290 |
+
with (
|
| 291 |
+
tracer.start_as_current_span("flux.generate_image") if tracer else nullcontext()
|
| 292 |
+
):
|
| 293 |
+
url = _get_flux_url()
|
| 294 |
+
resp = _httpx.post(
|
| 295 |
+
url,
|
| 296 |
+
json={"prompt": prompt, "steps": 30},
|
| 297 |
+
timeout=600,
|
| 298 |
+
follow_redirects=True,
|
| 299 |
+
)
|
| 300 |
+
resp.raise_for_status()
|
| 301 |
+
return resp.content
|
| 302 |
|
| 303 |
|
| 304 |
# ------ Caption Generation (direct LLM call with image prompt context) ------
|
| 305 |
|
| 306 |
|
| 307 |
+
def generate_caption(
|
| 308 |
+
corroborate: str, opposite: str, image_prompt: str, session_id: str | None = None
|
| 309 |
+
) -> dict:
|
| 310 |
"""
|
| 311 |
Send the research + image prompt to Gemma 4 and get a 30-second
|
| 312 |
spoken caption + voice style back.
|
|
|
|
| 354 |
}
|
| 355 |
|
| 356 |
with _using_session(session_id):
|
| 357 |
+
_span_cm = (
|
| 358 |
+
tracer.start_as_current_span("llm.generate_caption")
|
| 359 |
+
if tracer
|
| 360 |
+
else nullcontext()
|
|
|
|
| 361 |
)
|
| 362 |
+
with _span_cm as _span:
|
| 363 |
+
resp = _httpx.post(
|
| 364 |
+
f"{_VLLM_BASE_URL}/chat/completions",
|
| 365 |
+
json=payload,
|
| 366 |
+
headers={"Authorization": "Bearer sk-dummy-key-not-needed"},
|
| 367 |
+
timeout=300,
|
| 368 |
+
)
|
| 369 |
+
resp.raise_for_status()
|
| 370 |
+
body = resp.json()
|
| 371 |
+
text = body["choices"][0]["message"]["content"]
|
| 372 |
+
|
| 373 |
+
if tracer and "usage" in body:
|
| 374 |
+
usage = body["usage"]
|
| 375 |
+
_span.set_attribute(
|
| 376 |
+
"llm.token_count.prompt", usage.get("prompt_tokens", 0)
|
| 377 |
+
)
|
| 378 |
+
_span.set_attribute(
|
| 379 |
+
"llm.token_count.completion", usage.get("completion_tokens", 0)
|
| 380 |
+
)
|
| 381 |
+
_span.set_attribute(
|
| 382 |
+
"llm.token_count.total", usage.get("total_tokens", 0)
|
| 383 |
+
)
|
| 384 |
+
_span.set_attribute("llm.model_name", _VLLM_SERVED_MODEL)
|
| 385 |
|
| 386 |
print(f"Gemma response:\n{text}\n")
|
| 387 |
|
|
|
|
| 399 |
# ------ Voice Generation (VoxCPM on Modal) ------
|
| 400 |
|
| 401 |
|
| 402 |
+
def generate_voice(
|
| 403 |
+
voice_style: str, voice_script: str, session_id: str | None = None
|
| 404 |
+
) -> bytes:
|
| 405 |
"""Send script to VoxCPM on Modal T4, return WAV bytes."""
|
| 406 |
if session_id is None:
|
| 407 |
session_id = str(uuid.uuid4())
|
|
|
|
| 413 |
import httpx as _httpx
|
| 414 |
|
| 415 |
with _using_session(session_id):
|
| 416 |
+
with (
|
| 417 |
+
tracer.start_as_current_span("voxcpm.generate_voice")
|
| 418 |
+
if tracer
|
| 419 |
+
else nullcontext()
|
| 420 |
+
):
|
| 421 |
+
url = _get_vox_url()
|
| 422 |
+
resp = _httpx.post(
|
| 423 |
+
url,
|
| 424 |
+
json={"text": voice_script, "voice_style": voice_style},
|
| 425 |
+
timeout=600,
|
| 426 |
+
follow_redirects=True,
|
| 427 |
+
)
|
| 428 |
+
resp.raise_for_status()
|
| 429 |
+
return resp.content
|
| 430 |
|
| 431 |
|
| 432 |
# ------ Audio Transcription (Cohere Transcribe on Modal) ------
|
|
|
|
| 442 |
url = _get_transcribe_url()
|
| 443 |
|
| 444 |
with _using_session(session_id):
|
| 445 |
+
with (
|
| 446 |
+
tracer.start_as_current_span("cohere.transcribe_audio")
|
| 447 |
+
if tracer
|
| 448 |
+
else nullcontext()
|
| 449 |
+
):
|
| 450 |
+
with open(audio_path, "rb") as f:
|
| 451 |
+
audio_b64 = base64.b64encode(f.read()).decode()
|
| 452 |
|
| 453 |
+
resp = _httpx.post(url, json={"audio": audio_b64}, timeout=600)
|
| 454 |
+
resp.raise_for_status()
|
| 455 |
+
text = resp.json()["transcription"]
|
| 456 |
+
print(f'Transcribed: "{text}"\n')
|
| 457 |
+
return text
|
| 458 |
|
| 459 |
|
| 460 |
# ------ CLI Entry Point ------
|
|
|
|
| 503 |
caption_result = None
|
| 504 |
if prompt and corroborate and opposite:
|
| 505 |
try:
|
| 506 |
+
caption_result = generate_caption(
|
| 507 |
+
corroborate, opposite, prompt, session_id=session_id
|
| 508 |
+
)
|
| 509 |
if caption_result["caption"]:
|
| 510 |
print(f"\n✓ Caption: {caption_result['caption']}")
|
| 511 |
if caption_result["voice_style"]:
|
|
|
|
| 516 |
print("(Skipping caption generation — missing prompt or research)")
|
| 517 |
|
| 518 |
# Generate voice
|
| 519 |
+
if (
|
| 520 |
+
caption_result
|
| 521 |
+
and caption_result["voice_style"]
|
| 522 |
+
and caption_result["caption"]
|
| 523 |
+
):
|
| 524 |
try:
|
| 525 |
audio_bytes = generate_voice(
|
| 526 |
+
caption_result["voice_style"],
|
| 527 |
+
caption_result["caption"],
|
| 528 |
+
session_id=session_id,
|
| 529 |
)
|
| 530 |
filename = "crew_voice_output.wav"
|
| 531 |
with open(filename, "wb") as f:
|