hyperagents 1.2.3rc5.post3__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.
- agents/__init__.py +31 -0
- agents/background_monitor.py +453 -0
- agents/learning.py +460 -0
- agents/planner.py +421 -0
- agents/transcriber.py +341 -0
- civilization/__init__.py +51 -0
- civilization/api/__init__.py +7 -0
- civilization/api/civilization_api.py +361 -0
- civilization/graph/__init__.py +19 -0
- civilization/graph/knowledge_linker.py +348 -0
- civilization/graph/org_graph.py +348 -0
- civilization/graph/process_graph.py +386 -0
- civilization/ingestion/__init__.py +20 -0
- civilization/ingestion/chunker.py +273 -0
- civilization/ingestion/document_ingestor.py +358 -0
- civilization/ingestion/embedder.py +262 -0
- civilization/ingestion/extractor.py +224 -0
- civilization/interview/__init__.py +19 -0
- civilization/interview/gap_detector.py +380 -0
- civilization/interview/interviewer.py +323 -0
- civilization/interview/questioner.py +363 -0
- civilization/retrieval/__init__.py +17 -0
- civilization/retrieval/civilization_rag.py +316 -0
- civilization/retrieval/context_injector.py +342 -0
- civilization/retrieval/relevance_ranker.py +287 -0
- civilization/schema.py +299 -0
- civilization/store.py +177 -0
- civilization/sync/__init__.py +16 -0
- civilization/sync/confluence_sync.py +317 -0
- civilization/sync/gdrive_sync.py +262 -0
- civilization/sync/notion_sync.py +237 -0
- civilization/versioning/__init__.py +19 -0
- civilization/versioning/diff_engine.py +351 -0
- civilization/versioning/staleness_detector.py +303 -0
- civilization/versioning/version_manager.py +293 -0
- cli/__init__.py +1 -0
- cli/doctor.py +334 -0
- cli/hyperclaw.py +1357 -0
- cli/onboarding.py +469 -0
- core/__init__.py +1 -0
- core/connectors/__init__.py +119 -0
- core/connectors/infrastructure.py +772 -0
- core/connectors/messaging.py +586 -0
- core/connectors/productivity.py +764 -0
- core/connectors/smarthome.py +528 -0
- core/connectors/social.py +730 -0
- core/hyperrouter/__init__.py +23 -0
- core/hyperrouter/bandit.py +218 -0
- core/hyperrouter/fast_loop.py +68 -0
- core/hyperrouter/slow_loop.py +143 -0
- core/hyperstate/__init__.py +24 -0
- core/hyperstate/certifier.py +154 -0
- core/hyperstate/schema.py +160 -0
- core/hyperstate/state_manager.py +103 -0
- core/hyperstate/store.py +163 -0
- core/providers/__init__.py +121 -0
- core/providers/embeddings.py +389 -0
- core/providers/image.py +534 -0
- core/providers/llm.py +1935 -0
- core/providers/search.py +608 -0
- core/providers/voice.py +474 -0
- hyperagents-1.2.3rc5.post3.dist-info/METADATA +469 -0
- hyperagents-1.2.3rc5.post3.dist-info/RECORD +361 -0
- hyperagents-1.2.3rc5.post3.dist-info/WHEEL +5 -0
- hyperagents-1.2.3rc5.post3.dist-info/entry_points.txt +6 -0
- hyperagents-1.2.3rc5.post3.dist-info/licenses/LICENSE +21 -0
- hyperagents-1.2.3rc5.post3.dist-info/top_level.txt +13 -0
- hyperclaw/__init__.py +67 -0
- hyperclaw/__main__.py +220 -0
- hyperclaw/agent.py +775 -0
- hyperclaw/agent_coordinator.py +577 -0
- hyperclaw/anthropic_params.py +225 -0
- hyperclaw/api_utils.py +85 -0
- hyperclaw/approval_telegram.py +209 -0
- hyperclaw/approval_terminal.py +80 -0
- hyperclaw/attachments.py +177 -0
- hyperclaw/branding.py +55 -0
- hyperclaw/brief/__init__.py +11 -0
- hyperclaw/brief/cli.py +204 -0
- hyperclaw/brief/collect.py +146 -0
- hyperclaw/brief/compose.py +333 -0
- hyperclaw/brief/diff.py +110 -0
- hyperclaw/brief/errors.py +99 -0
- hyperclaw/brief/google.py +269 -0
- hyperclaw/brief/google_auth.py +294 -0
- hyperclaw/brief/links.py +124 -0
- hyperclaw/brief/service.py +424 -0
- hyperclaw/brief/store.py +205 -0
- hyperclaw/brief/tasks.py +200 -0
- hyperclaw/chat_scope.py +115 -0
- hyperclaw/cli_tui.py +497 -0
- hyperclaw/client_guard.py +717 -0
- hyperclaw/connection_check.py +113 -0
- hyperclaw/cost_guard.py +1728 -0
- hyperclaw/default_config/agents.yaml +534 -0
- hyperclaw/default_config/hyperclaw.yaml +27 -0
- hyperclaw/default_config/models.yaml +161 -0
- hyperclaw/deployment.py +39 -0
- hyperclaw/digest/__init__.py +9 -0
- hyperclaw/digest/cli.py +117 -0
- hyperclaw/digest/errors.py +100 -0
- hyperclaw/digest/gitlab.py +187 -0
- hyperclaw/digest/http.py +113 -0
- hyperclaw/digest/redact.py +32 -0
- hyperclaw/digest/service.py +271 -0
- hyperclaw/digest/slack.py +223 -0
- hyperclaw/digest/state.py +124 -0
- hyperclaw/digest/summarize.py +198 -0
- hyperclaw/digest/tokens.py +143 -0
- hyperclaw/distribution.py +46 -0
- hyperclaw/documents.py +351 -0
- hyperclaw/feeds.py +339 -0
- hyperclaw/google_workspace/__init__.py +5 -0
- hyperclaw/google_workspace/api.py +151 -0
- hyperclaw/google_workspace/auth.py +397 -0
- hyperclaw/google_workspace/calendar.py +157 -0
- hyperclaw/google_workspace/cli.py +72 -0
- hyperclaw/google_workspace/drive.py +236 -0
- hyperclaw/google_workspace/errors.py +58 -0
- hyperclaw/google_workspace/gmail.py +295 -0
- hyperclaw/google_workspace/meet.py +122 -0
- hyperclaw/google_workspace/tools.py +319 -0
- hyperclaw/hyperspeed.py +57 -0
- hyperclaw/integrations_layer.py +1427 -0
- hyperclaw/live_state.py +188 -0
- hyperclaw/managed_config.py +245 -0
- hyperclaw/media_hub.py +179 -0
- hyperclaw/memory_manager.py +584 -0
- hyperclaw/model_catalog.py +551 -0
- hyperclaw/model_policy.py +211 -0
- hyperclaw/model_router.py +595 -0
- hyperclaw/model_selector.py +72 -0
- hyperclaw/naming.py +284 -0
- hyperclaw/notices.py +65 -0
- hyperclaw/oai_stream.py +106 -0
- hyperclaw/orchestrator.py +591 -0
- hyperclaw/outbox.py +82 -0
- hyperclaw/persona.py +367 -0
- hyperclaw/prometheus.py +219 -0
- hyperclaw/prompt_assembly.py +157 -0
- hyperclaw/provider_setup.py +146 -0
- hyperclaw/providers.py +340 -0
- hyperclaw/qualification.py +341 -0
- hyperclaw/qualification_keys/hyper-d292fd127cef5a3e.pub +1 -0
- hyperclaw/qualification_records/anthropic--claude-sonnet-5-5.json +14 -0
- hyperclaw/reasoning_display.py +35 -0
- hyperclaw/scheduler.py +454 -0
- hyperclaw/server.py +847 -0
- hyperclaw/setup.py +627 -0
- hyperclaw/skills.py +717 -0
- hyperclaw/solomon.py +442 -0
- hyperclaw/swarm.py +533 -0
- hyperclaw/taint.py +832 -0
- hyperclaw/team/__init__.py +28 -0
- hyperclaw/team/dispatcher.py +431 -0
- hyperclaw/team/genesis.py +106 -0
- hyperclaw/team/lead.py +110 -0
- hyperclaw/team/model.py +135 -0
- hyperclaw/team/morning.py +238 -0
- hyperclaw/team/repos.py +66 -0
- hyperclaw/team/roster.py +264 -0
- hyperclaw/team/specialist.py +235 -0
- hyperclaw/team/status.py +163 -0
- hyperclaw/team/watchers.py +112 -0
- hyperclaw/teamsetup/__init__.py +17 -0
- hyperclaw/teamsetup/checks.py +208 -0
- hyperclaw/teamsetup/item.py +391 -0
- hyperclaw/teamsetup/oauth_loopback.py +84 -0
- hyperclaw/teamsetup/onboard.py +550 -0
- hyperclaw/teamsetup/profile.py +82 -0
- hyperclaw/teamsetup/secrets.py +313 -0
- hyperclaw/teamsetup/selfcheck.py +295 -0
- hyperclaw/teamsetup/service.py +495 -0
- hyperclaw/teamsetup/slack_oauth.py +442 -0
- hyperclaw/teamsetup/tty.py +62 -0
- hyperclaw/telegram_bot.py +1106 -0
- hyperclaw/telegram_intake.py +126 -0
- hyperclaw/telegram_pairing.py +360 -0
- hyperclaw/terminal_provider.py +323 -0
- hyperclaw/tools/__init__.py +1 -0
- hyperclaw/tools/approval.py +949 -0
- hyperclaw/tools/args.py +212 -0
- hyperclaw/tools/catalog.py +244 -0
- hyperclaw/tools/classify.py +266 -0
- hyperclaw/tools/effects.py +724 -0
- hyperclaw/tools/egress.py +210 -0
- hyperclaw/tools/envelope.py +188 -0
- hyperclaw/tools/guard.py +367 -0
- hyperclaw/tools/ledger.py +339 -0
- hyperclaw/tools/receipts.py +70 -0
- hyperclaw/tools/registry.py +318 -0
- hyperclaw/tools/service_guard.py +151 -0
- hyperclaw/tools/surfaces.py +54 -0
- hyperclaw/tools/workspace.py +87 -0
- hyperclaw/trading/__init__.py +77 -0
- hyperclaw/truth_guard.py +791 -0
- hyperclaw/tts.py +124 -0
- hyperclaw/tui.py +4255 -0
- hyperclaw/tui_bridge.py +938 -0
- hyperclaw/unattended.py +60 -0
- hyperclaw/updates/__init__.py +54 -0
- hyperclaw/updates/check.py +131 -0
- hyperclaw/updates/index.py +178 -0
- hyperclaw/updates/install.py +110 -0
- hyperclaw/updates/managed.py +161 -0
- hyperclaw/updates/manifest.py +138 -0
- hyperclaw/updates/notices.py +86 -0
- hyperclaw/updates/paths.py +53 -0
- hyperclaw/updates/release.py +146 -0
- hyperclaw/updates/upgrade.py +253 -0
- hyperclaw/updates/versioning.py +104 -0
- hyperclaw/userconfig.py +119 -0
- hyperclaw/xai.py +83 -0
- integrations/__init__.py +31 -0
- integrations/automation/__init__.py +1 -0
- integrations/automation/make.py +87 -0
- integrations/automation/zapier.py +87 -0
- integrations/base.py +476 -0
- integrations/communication/__init__.py +1 -0
- integrations/communication/twilio.py +279 -0
- integrations/communication/zoom.py +290 -0
- integrations/data/__init__.py +1 -0
- integrations/data/google_analytics.py +135 -0
- integrations/data/postgres.py +182 -0
- integrations/data/supabase_connector.py +202 -0
- integrations/data/weather_feeds.py +450 -0
- integrations/developer/__init__.py +9 -0
- integrations/developer/github.py +406 -0
- integrations/developer/gitlab.py +298 -0
- integrations/enterprise/__init__.py +13 -0
- integrations/enterprise/confluence.py +266 -0
- integrations/enterprise/hubspot.py +293 -0
- integrations/enterprise/jira.py +314 -0
- integrations/enterprise/salesforce.py +286 -0
- integrations/finance/__init__.py +1 -0
- integrations/finance/polymarket_client.py +247 -0
- integrations/finance/quickbooks.py +281 -0
- integrations/finance/stripe.py +261 -0
- integrations/gateway.py +238 -0
- integrations/messaging/__init__.py +32 -0
- integrations/messaging/discord.py +159 -0
- integrations/messaging/email.py +401 -0
- integrations/messaging/imessage.py +443 -0
- integrations/messaging/signal.py +175 -0
- integrations/messaging/slack.py +217 -0
- integrations/messaging/sms.py +175 -0
- integrations/messaging/teams.py +208 -0
- integrations/messaging/telegram.py +260 -0
- integrations/messaging/whatsapp.py +203 -0
- integrations/productivity/__init__.py +17 -0
- integrations/productivity/airtable.py +217 -0
- integrations/productivity/asana.py +248 -0
- integrations/productivity/google/__init__.py +19 -0
- integrations/productivity/google/base.py +89 -0
- integrations/productivity/google/calendar.py +272 -0
- integrations/productivity/google/docs.py +202 -0
- integrations/productivity/google/drive.py +268 -0
- integrations/productivity/google/gmail.py +279 -0
- integrations/productivity/google/meet.py +218 -0
- integrations/productivity/google/sheets.py +222 -0
- integrations/productivity/google/tasks.py +199 -0
- integrations/productivity/linear.py +279 -0
- integrations/productivity/microsoft/__init__.py +13 -0
- integrations/productivity/microsoft/base.py +93 -0
- integrations/productivity/microsoft/calendar.py +207 -0
- integrations/productivity/microsoft/onedrive.py +222 -0
- integrations/productivity/microsoft/outlook.py +241 -0
- integrations/productivity/microsoft/sharepoint.py +211 -0
- integrations/productivity/notion.py +243 -0
- integrations/productivity/todoist.py +250 -0
- integrations/productivity/trello.py +244 -0
- integrations/storage/__init__.py +1 -0
- integrations/storage/box.py +279 -0
- integrations/storage/dropbox.py +215 -0
- integrations/telegram_inbound.py +179 -0
- integrations/telegram_poller.py +151 -0
- memory/__init__.py +1 -0
- memory/causal_graph.py +125 -0
- memory/impact_tracker.py +88 -0
- memory/vector_memory.py +436 -0
- models/__init__.py +12 -0
- models/chatjimmy_client.py +210 -0
- models/claude_client.py +235 -0
- models/claude_code_subagent.py +192 -0
- models/openai_client.py +120 -0
- models/router.py +162 -0
- recursive/discovery_loop.py +118 -0
- recursive/optimization_loop.py +135 -0
- recursive/validation_loop.py +38 -0
- security/__init__.py +1 -0
- security/audit_logger.py +106 -0
- security/filesystem_guard.py +100 -0
- security/hypershield.py +99 -0
- security/network_guard.py +97 -0
- security/policy_engine.py +198 -0
- swarm/__init__.py +1 -0
- swarm/agents/__init__.py +0 -0
- swarm/agents/base.py +230 -0
- swarm/agents/business/__init__.py +0 -0
- swarm/agents/business/counsel.py +31 -0
- swarm/agents/business/herald.py +32 -0
- swarm/agents/business/ledger.py +26 -0
- swarm/agents/business/nexus.py +30 -0
- swarm/agents/business/ops.py +30 -0
- swarm/agents/business/pipeline.py +28 -0
- swarm/agents/business/revenue.py +30 -0
- swarm/agents/business/sovereign.py +31 -0
- swarm/agents/business/strategos.py +27 -0
- swarm/agents/business/talent.py +27 -0
- swarm/agents/business/venture.py +32 -0
- swarm/agents/comms/__init__.py +1 -0
- swarm/agents/comms/cipher.py +32 -0
- swarm/agents/comms/echo_comms.py +31 -0
- swarm/agents/comms/envoy.py +31 -0
- swarm/agents/comms/herald_scheduler.py +33 -0
- swarm/agents/comms/pulse.py +29 -0
- swarm/agents/creative/__init__.py +0 -0
- swarm/agents/creative/author.py +27 -0
- swarm/agents/creative/lens.py +25 -0
- swarm/agents/executive/__init__.py +1 -0
- swarm/agents/intelligence/__init__.py +1 -0
- swarm/agents/intelligence/arbiter.py +30 -0
- swarm/agents/intelligence/sentinel.py +30 -0
- swarm/agents/media/__init__.py +0 -0
- swarm/agents/personal/__init__.py +0 -0
- swarm/agents/personal/atlas.py +27 -0
- swarm/agents/personal/hearth.py +26 -0
- swarm/agents/personal/midas.py +27 -0
- swarm/agents/personal/navigator.py +27 -0
- swarm/agents/personal/nourish.py +31 -0
- swarm/agents/personal/vitals.py +33 -0
- swarm/agents/recursive/__init__.py +0 -0
- swarm/agents/recursive/alchemist.py +73 -0
- swarm/agents/recursive/calibrator.py +45 -0
- swarm/agents/recursive/scout.py +97 -0
- swarm/agents/scientific/__init__.py +0 -0
- swarm/agents/scientific/cosmos.py +36 -0
- swarm/agents/scientific/gaia.py +26 -0
- swarm/agents/scientific/medicus.py +33 -0
- swarm/agents/scientific/oracle_agent.py +39 -0
- swarm/agents/scientific/scribe.py +39 -0
- swarm/agents/talent/__init__.py +0 -0
- swarm/agents/talent/deal.py +28 -0
- swarm/agents/talent/roster.py +29 -0
- swarm/agents/talent/scout.py +29 -0
- swarm/agents/talent/stage.py +27 -0
- swarm/agents/tech/__init__.py +0 -0
- swarm/agents/tech/aegis.py +28 -0
- swarm/agents/tech/bridge.py +28 -0
- swarm/agents/tech/forge.py +28 -0
- swarm/agents/trading/__init__.py +1 -0
- swarm/agents/trading/global_prediction_engine.py +611 -0
- swarm/agents/trading/polymarket_trader.py +165 -0
- swarm/agents/trading/prediction_strategist.py +218 -0
- swarm/autogen_bridge.py +68 -0
- swarm/bid_protocol.py +196 -0
- swarm/dispatcher.py +527 -0
- swarm/nexus.py +181 -0
- swarm/registry.py +112 -0
- telegram_listener.py +403 -0
- ui/tui/app.py +214 -0
agents/transcriber.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Assistant Audio/Video Transcription — Transcribe calls, meetings, videos.
|
|
4
|
+
Uses OpenAI Whisper API or local whisper.cpp.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import subprocess
|
|
11
|
+
import tempfile
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Optional, Dict, List
|
|
15
|
+
import httpx
|
|
16
|
+
|
|
17
|
+
# Use user's ~/.hyperclaw directory, not package location
|
|
18
|
+
HYPERCLAW_ROOT = Path(os.environ.get("HYPERCLAW_ROOT", Path.home() / ".hyperclaw"))
|
|
19
|
+
TRANSCRIPTS_DIR = HYPERCLAW_ROOT / "workspace" / "transcripts"
|
|
20
|
+
TRANSCRIPTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def load_env():
|
|
24
|
+
"""Load environment from .env file."""
|
|
25
|
+
env_file = HYPERCLAW_ROOT / ".env"
|
|
26
|
+
if env_file.exists():
|
|
27
|
+
for line in env_file.read_text().splitlines():
|
|
28
|
+
if "=" in line and not line.startswith("#"):
|
|
29
|
+
key, val = line.split("=", 1)
|
|
30
|
+
os.environ.setdefault(key.strip(), val.strip().strip('"'))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
load_env()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_openai_key():
|
|
37
|
+
return os.environ.get("OPENAI_API_KEY", "")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def transcribe_audio(
|
|
41
|
+
audio_path: str,
|
|
42
|
+
language: str = None,
|
|
43
|
+
prompt: str = None,
|
|
44
|
+
output_format: str = "text"
|
|
45
|
+
) -> Dict:
|
|
46
|
+
"""Transcribe audio file using OpenAI Whisper API."""
|
|
47
|
+
api_key = get_openai_key()
|
|
48
|
+
if not api_key:
|
|
49
|
+
return {"error": "OPENAI_API_KEY not set"}
|
|
50
|
+
|
|
51
|
+
path = Path(audio_path).expanduser()
|
|
52
|
+
if not path.exists():
|
|
53
|
+
return {"error": f"File not found: {audio_path}"}
|
|
54
|
+
|
|
55
|
+
# Check file size (max 25MB for Whisper API)
|
|
56
|
+
file_size = path.stat().st_size
|
|
57
|
+
if file_size > 25 * 1024 * 1024:
|
|
58
|
+
return {"error": f"File too large ({file_size / 1024 / 1024:.1f}MB). Max 25MB. Use split_audio first."}
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
with open(path, "rb") as f:
|
|
62
|
+
files = {"file": (path.name, f, "audio/mpeg")}
|
|
63
|
+
data = {"model": "whisper-1"}
|
|
64
|
+
|
|
65
|
+
if language:
|
|
66
|
+
data["language"] = language
|
|
67
|
+
if prompt:
|
|
68
|
+
data["prompt"] = prompt
|
|
69
|
+
if output_format in ["json", "verbose_json", "srt", "vtt"]:
|
|
70
|
+
data["response_format"] = output_format
|
|
71
|
+
|
|
72
|
+
with httpx.Client(timeout=300) as client: # 5 min timeout for long files
|
|
73
|
+
resp = client.post(
|
|
74
|
+
"https://api.openai.com/v1/audio/transcriptions",
|
|
75
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
76
|
+
files=files,
|
|
77
|
+
data=data
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if resp.status_code == 200:
|
|
81
|
+
if output_format in ["json", "verbose_json"]:
|
|
82
|
+
result = resp.json()
|
|
83
|
+
else:
|
|
84
|
+
result = {"text": resp.text}
|
|
85
|
+
|
|
86
|
+
# Save transcript
|
|
87
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
88
|
+
transcript_path = TRANSCRIPTS_DIR / f"{path.stem}_{timestamp}.txt"
|
|
89
|
+
transcript_path.write_text(result.get("text", str(result)))
|
|
90
|
+
|
|
91
|
+
result["saved_to"] = str(transcript_path)
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
return {"error": f"API error: {resp.status_code} - {resp.text}"}
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
return {"error": str(e)}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def transcribe_video(video_path: str, **kwargs) -> Dict:
|
|
101
|
+
"""Transcribe video by extracting audio first."""
|
|
102
|
+
path = Path(video_path).expanduser()
|
|
103
|
+
if not path.exists():
|
|
104
|
+
return {"error": f"File not found: {video_path}"}
|
|
105
|
+
|
|
106
|
+
# Extract audio using ffmpeg
|
|
107
|
+
try:
|
|
108
|
+
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as tmp:
|
|
109
|
+
tmp_path = tmp.name
|
|
110
|
+
|
|
111
|
+
result = subprocess.run([
|
|
112
|
+
"ffmpeg", "-i", str(path),
|
|
113
|
+
"-vn", "-acodec", "libmp3lame", "-q:a", "4",
|
|
114
|
+
"-y", tmp_path
|
|
115
|
+
], capture_output=True, text=True, timeout=300)
|
|
116
|
+
|
|
117
|
+
if result.returncode != 0:
|
|
118
|
+
return {"error": f"ffmpeg failed: {result.stderr}"}
|
|
119
|
+
|
|
120
|
+
# Transcribe the extracted audio
|
|
121
|
+
transcript = transcribe_audio(tmp_path, **kwargs)
|
|
122
|
+
|
|
123
|
+
# Clean up
|
|
124
|
+
Path(tmp_path).unlink()
|
|
125
|
+
|
|
126
|
+
return transcript
|
|
127
|
+
|
|
128
|
+
except FileNotFoundError:
|
|
129
|
+
return {"error": "ffmpeg not installed. Install with: brew install ffmpeg"}
|
|
130
|
+
except Exception as e:
|
|
131
|
+
return {"error": str(e)}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def record_audio(duration: int = 30, output_path: str = None) -> Dict:
|
|
135
|
+
"""Record audio from microphone."""
|
|
136
|
+
if not output_path:
|
|
137
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
138
|
+
output_path = str(TRANSCRIPTS_DIR / f"recording_{timestamp}.wav")
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# Use macOS sox or ffmpeg to record
|
|
142
|
+
result = subprocess.run([
|
|
143
|
+
"sox", "-d", "-r", "16000", "-c", "1", output_path,
|
|
144
|
+
"trim", "0", str(duration)
|
|
145
|
+
], capture_output=True, text=True, timeout=duration + 10)
|
|
146
|
+
|
|
147
|
+
if result.returncode == 0:
|
|
148
|
+
return {"recorded": output_path, "duration": duration}
|
|
149
|
+
|
|
150
|
+
# Fallback to ffmpeg
|
|
151
|
+
result = subprocess.run([
|
|
152
|
+
"ffmpeg", "-f", "avfoundation", "-i", ":0",
|
|
153
|
+
"-t", str(duration), "-y", output_path
|
|
154
|
+
], capture_output=True, text=True, timeout=duration + 10)
|
|
155
|
+
|
|
156
|
+
if result.returncode == 0:
|
|
157
|
+
return {"recorded": output_path, "duration": duration}
|
|
158
|
+
|
|
159
|
+
return {"error": "Recording failed. Install sox: brew install sox"}
|
|
160
|
+
|
|
161
|
+
except Exception as e:
|
|
162
|
+
return {"error": str(e)}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def record_and_transcribe(duration: int = 30) -> Dict:
|
|
166
|
+
"""Record audio and transcribe it."""
|
|
167
|
+
# Record
|
|
168
|
+
record_result = record_audio(duration)
|
|
169
|
+
if "error" in record_result:
|
|
170
|
+
return record_result
|
|
171
|
+
|
|
172
|
+
# Transcribe
|
|
173
|
+
transcript = transcribe_audio(record_result["recorded"])
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
"recorded": record_result["recorded"],
|
|
177
|
+
"duration": duration,
|
|
178
|
+
"transcript": transcript.get("text", ""),
|
|
179
|
+
"saved_to": transcript.get("saved_to")
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def split_audio(audio_path: str, chunk_minutes: int = 10) -> List[str]:
|
|
184
|
+
"""Split large audio file into chunks for transcription."""
|
|
185
|
+
path = Path(audio_path).expanduser()
|
|
186
|
+
if not path.exists():
|
|
187
|
+
return []
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
chunks = []
|
|
191
|
+
chunk_dir = TRANSCRIPTS_DIR / f"chunks_{path.stem}"
|
|
192
|
+
chunk_dir.mkdir(exist_ok=True)
|
|
193
|
+
|
|
194
|
+
# Get duration
|
|
195
|
+
result = subprocess.run([
|
|
196
|
+
"ffprobe", "-v", "error", "-show_entries", "format=duration",
|
|
197
|
+
"-of", "default=noprint_wrappers=1:nokey=1", str(path)
|
|
198
|
+
], capture_output=True, text=True)
|
|
199
|
+
|
|
200
|
+
duration = float(result.stdout.strip())
|
|
201
|
+
chunk_seconds = chunk_minutes * 60
|
|
202
|
+
|
|
203
|
+
for i, start in enumerate(range(0, int(duration), chunk_seconds)):
|
|
204
|
+
chunk_path = chunk_dir / f"chunk_{i:03d}.mp3"
|
|
205
|
+
subprocess.run([
|
|
206
|
+
"ffmpeg", "-i", str(path),
|
|
207
|
+
"-ss", str(start), "-t", str(chunk_seconds),
|
|
208
|
+
"-acodec", "libmp3lame", "-q:a", "4",
|
|
209
|
+
"-y", str(chunk_path)
|
|
210
|
+
], capture_output=True, timeout=120)
|
|
211
|
+
|
|
212
|
+
if chunk_path.exists():
|
|
213
|
+
chunks.append(str(chunk_path))
|
|
214
|
+
|
|
215
|
+
return chunks
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
return []
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def transcribe_long_audio(audio_path: str, chunk_minutes: int = 10) -> Dict:
|
|
222
|
+
"""Transcribe long audio by splitting into chunks."""
|
|
223
|
+
chunks = split_audio(audio_path, chunk_minutes)
|
|
224
|
+
if not chunks:
|
|
225
|
+
return {"error": "Failed to split audio"}
|
|
226
|
+
|
|
227
|
+
full_transcript = []
|
|
228
|
+
for i, chunk in enumerate(chunks):
|
|
229
|
+
print(f"Transcribing chunk {i+1}/{len(chunks)}...")
|
|
230
|
+
result = transcribe_audio(chunk)
|
|
231
|
+
if "text" in result:
|
|
232
|
+
full_transcript.append(result["text"])
|
|
233
|
+
|
|
234
|
+
# Combine and save
|
|
235
|
+
combined_text = "\n\n".join(full_transcript)
|
|
236
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
237
|
+
output_path = TRANSCRIPTS_DIR / f"{Path(audio_path).stem}_full_{timestamp}.txt"
|
|
238
|
+
output_path.write_text(combined_text)
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
"text": combined_text,
|
|
242
|
+
"chunks": len(chunks),
|
|
243
|
+
"saved_to": str(output_path)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def list_transcripts() -> List[Dict]:
|
|
248
|
+
"""List all saved transcripts."""
|
|
249
|
+
transcripts = []
|
|
250
|
+
for path in TRANSCRIPTS_DIR.glob("*.txt"):
|
|
251
|
+
transcripts.append({
|
|
252
|
+
"name": path.name,
|
|
253
|
+
"path": str(path),
|
|
254
|
+
"size": path.stat().st_size,
|
|
255
|
+
"created": datetime.fromtimestamp(path.stat().st_ctime).isoformat()
|
|
256
|
+
})
|
|
257
|
+
return sorted(transcripts, key=lambda x: x["created"], reverse=True)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def get_transcript(name: str) -> str:
|
|
261
|
+
"""Get a transcript by name."""
|
|
262
|
+
path = TRANSCRIPTS_DIR / name
|
|
263
|
+
if path.exists():
|
|
264
|
+
return path.read_text()
|
|
265
|
+
|
|
266
|
+
# Try partial match
|
|
267
|
+
for p in TRANSCRIPTS_DIR.glob(f"*{name}*.txt"):
|
|
268
|
+
return p.read_text()
|
|
269
|
+
|
|
270
|
+
return "Transcript not found"
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def summarize_transcript(transcript_text: str) -> str:
|
|
274
|
+
"""Summarize a transcript using Claude (placeholder)."""
|
|
275
|
+
# This would call Claude API to summarize
|
|
276
|
+
# For now, return word count
|
|
277
|
+
words = len(transcript_text.split())
|
|
278
|
+
return f"Transcript has {words} words. Full summarization requires Claude API call."
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# CLI functions for TUI integration
|
|
282
|
+
def audio_transcribe(path: str, language: str = None) -> Dict:
|
|
283
|
+
"""Transcribe an audio file."""
|
|
284
|
+
return transcribe_audio(path, language=language)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def video_transcribe(path: str, language: str = None) -> Dict:
|
|
288
|
+
"""Transcribe a video file."""
|
|
289
|
+
return transcribe_video(path, language=language)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def audio_record(duration: int = 30) -> Dict:
|
|
293
|
+
"""Record audio from microphone."""
|
|
294
|
+
return record_audio(duration)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def audio_record_transcribe(duration: int = 30) -> Dict:
|
|
298
|
+
"""Record and transcribe."""
|
|
299
|
+
return record_and_transcribe(duration)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def transcripts_list() -> List[Dict]:
|
|
303
|
+
"""List all transcripts."""
|
|
304
|
+
return list_transcripts()
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def transcript_get(name: str) -> str:
|
|
308
|
+
"""Get a specific transcript."""
|
|
309
|
+
return get_transcript(name)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
if __name__ == "__main__":
|
|
313
|
+
import sys
|
|
314
|
+
|
|
315
|
+
if len(sys.argv) < 2:
|
|
316
|
+
print("Usage: transcriber.py <transcribe|record|list> [args]")
|
|
317
|
+
sys.exit(1)
|
|
318
|
+
|
|
319
|
+
cmd = sys.argv[1]
|
|
320
|
+
|
|
321
|
+
if cmd == "transcribe":
|
|
322
|
+
path = sys.argv[2] if len(sys.argv) > 2 else ""
|
|
323
|
+
result = transcribe_audio(path)
|
|
324
|
+
print(json.dumps(result, indent=2))
|
|
325
|
+
|
|
326
|
+
elif cmd == "video":
|
|
327
|
+
path = sys.argv[2] if len(sys.argv) > 2 else ""
|
|
328
|
+
result = transcribe_video(path)
|
|
329
|
+
print(json.dumps(result, indent=2))
|
|
330
|
+
|
|
331
|
+
elif cmd == "record":
|
|
332
|
+
duration = int(sys.argv[2]) if len(sys.argv) > 2 else 30
|
|
333
|
+
result = record_and_transcribe(duration)
|
|
334
|
+
print(json.dumps(result, indent=2))
|
|
335
|
+
|
|
336
|
+
elif cmd == "list":
|
|
337
|
+
for t in list_transcripts():
|
|
338
|
+
print(f"{t['name']} ({t['size']} bytes)")
|
|
339
|
+
|
|
340
|
+
else:
|
|
341
|
+
print(f"Unknown command: {cmd}")
|
civilization/__init__.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Civilization Knowledge Layer — organizational knowledge graph for HyperClaw.
|
|
3
|
+
Stores SOPs, job descriptions, org charts, workflows, runbooks, and more.
|
|
4
|
+
"""
|
|
5
|
+
from .schema import (
|
|
6
|
+
NodeType,
|
|
7
|
+
NodeStatus,
|
|
8
|
+
CivilizationNode,
|
|
9
|
+
SOP,
|
|
10
|
+
SOPStep,
|
|
11
|
+
JobDescription,
|
|
12
|
+
Role,
|
|
13
|
+
Person,
|
|
14
|
+
Checklist,
|
|
15
|
+
ChecklistItem,
|
|
16
|
+
Runbook,
|
|
17
|
+
RunbookStep,
|
|
18
|
+
Workflow,
|
|
19
|
+
WorkflowNode,
|
|
20
|
+
WorkflowEdge,
|
|
21
|
+
OrgChart,
|
|
22
|
+
OrgUnit,
|
|
23
|
+
ClientProfile,
|
|
24
|
+
PersonalRoutine,
|
|
25
|
+
RoutineBlock,
|
|
26
|
+
)
|
|
27
|
+
from .store import CivilizationStore
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"NodeType",
|
|
31
|
+
"NodeStatus",
|
|
32
|
+
"CivilizationNode",
|
|
33
|
+
"SOP",
|
|
34
|
+
"SOPStep",
|
|
35
|
+
"JobDescription",
|
|
36
|
+
"Role",
|
|
37
|
+
"Person",
|
|
38
|
+
"Checklist",
|
|
39
|
+
"ChecklistItem",
|
|
40
|
+
"Runbook",
|
|
41
|
+
"RunbookStep",
|
|
42
|
+
"Workflow",
|
|
43
|
+
"WorkflowNode",
|
|
44
|
+
"WorkflowEdge",
|
|
45
|
+
"OrgChart",
|
|
46
|
+
"OrgUnit",
|
|
47
|
+
"ClientProfile",
|
|
48
|
+
"PersonalRoutine",
|
|
49
|
+
"RoutineBlock",
|
|
50
|
+
"CivilizationStore",
|
|
51
|
+
]
|