pal-code 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.
- pal/__init__.py +29 -0
- pal/__main__.py +3 -0
- pal/access_manager/__init__.py +16 -0
- pal/access_manager/components/__init__.py +14 -0
- pal/access_manager/components/access_control.py +41 -0
- pal/access_manager/components/base.py +39 -0
- pal/access_manager/components/intervention.py +31 -0
- pal/access_manager/components/privilege_registry.py +30 -0
- pal/access_manager/pipeline.py +68 -0
- pal/access_manager/utils/__init__.py +2 -0
- pal/agent_constraints/__init__.py +3 -0
- pal/agent_constraints/components/AGENT_OUTPUT.md +13 -0
- pal/agent_constraints/components/__init__.py +3 -0
- pal/agent_constraints/components/constraints.py +22 -0
- pal/agent_constraints/pipeline.py +22 -0
- pal/agent_constraints/utils/__init__.py +2 -0
- pal/agents/__init__.py +12 -0
- pal/agents/coding.py +14 -0
- pal/agents/docs.py +15 -0
- pal/chat/__init__.py +4 -0
- pal/chat/demo.py +231 -0
- pal/chat/providers.py +23 -0
- pal/chat/service.py +591 -0
- pal/chat/services/__init__.py +26 -0
- pal/chat/services/base.py +142 -0
- pal/chat/services/data.py +142 -0
- pal/chat/services/media.py +123 -0
- pal/chat/services/web.py +60 -0
- pal/chat/sessions.py +108 -0
- pal/chat/tools.py +446 -0
- pal/chat/uploads.py +81 -0
- pal/cli.py +502 -0
- pal/compression/__init__.py +12 -0
- pal/compression/components/__init__.py +64 -0
- pal/compression/components/adverbs.py +20 -0
- pal/compression/components/hedges.py +54 -0
- pal/compression/components/interjections.py +20 -0
- pal/compression/components/minifier.py +56 -0
- pal/compression/components/output_compressor.py +13 -0
- pal/compression/components/pleonasms.py +54 -0
- pal/compression/components/punctuations.py +36 -0
- pal/compression/components/stopwords.py +62 -0
- pal/compression/history.py +346 -0
- pal/compression/pipeline.py +58 -0
- pal/compression/utils/__init__.py +30 -0
- pal/compression/utils/negations.py +18 -0
- pal/compression/utils/nltk_setup.py +20 -0
- pal/compression/utils/pronouns.py +27 -0
- pal/compression/utils/proselint_setup.py +9 -0
- pal/compression/utils/spacy_setup.py +22 -0
- pal/config/default.yaml +163 -0
- pal/config.py +504 -0
- pal/context_manager/__init__.py +7 -0
- pal/context_manager/chat_context.py +117 -0
- pal/context_manager/components/__init__.py +5 -0
- pal/context_manager/components/assembler.py +75 -0
- pal/context_manager/components/context_cache.py +45 -0
- pal/context_manager/components/kv_bridge.py +16 -0
- pal/context_manager/components/pager.py +48 -0
- pal/context_manager/components/simple_context_manager.py +56 -0
- pal/context_manager/components/turns.py +88 -0
- pal/context_manager/pipeline.py +27 -0
- pal/context_manager/utils/__init__.py +18 -0
- pal/context_manager/utils/hashing.py +5 -0
- pal/context_manager/utils/relevance.py +59 -0
- pal/context_manager/utils/storage.py +37 -0
- pal/documents/__init__.py +4 -0
- pal/documents/components/__init__.py +0 -0
- pal/documents/components/chunker.py +79 -0
- pal/documents/components/index.py +155 -0
- pal/documents/utils/__init__.py +1 -0
- pal/errors.py +40 -0
- pal/input_converters/__init__.py +8 -0
- pal/input_converters/components/__init__.py +0 -0
- pal/input_converters/components/base.py +38 -0
- pal/input_converters/components/pandoc_converter.py +87 -0
- pal/input_converters/components/pdf_converter.py +46 -0
- pal/input_converters/components/pptx_converter.py +52 -0
- pal/input_converters/components/xlsx_converter.py +38 -0
- pal/input_converters/pipeline.py +72 -0
- pal/input_converters/utils/__init__.py +1 -0
- pal/input_converters/workspace.py +56 -0
- pal/input_savers/INPUT_RULES.md +2 -0
- pal/input_savers/__init__.py +5 -0
- pal/input_savers/components/__init__.py +0 -0
- pal/input_savers/components/base.py +40 -0
- pal/input_savers/components/methods.py +41 -0
- pal/input_savers/components/verifier.py +39 -0
- pal/input_savers/pipeline.py +154 -0
- pal/input_savers/utils/__init__.py +0 -0
- pal/input_savers/utils/guards.py +94 -0
- pal/knowledge_graph/__init__.py +10 -0
- pal/knowledge_graph/components/__init__.py +0 -0
- pal/knowledge_graph/components/base.py +50 -0
- pal/knowledge_graph/components/builder.py +54 -0
- pal/knowledge_graph/components/retriever.py +100 -0
- pal/knowledge_graph/components/workspace.py +116 -0
- pal/knowledge_graph/pipeline.py +215 -0
- pal/knowledge_graph/utils/__init__.py +1 -0
- pal/latency_tracker/__init__.py +3 -0
- pal/latency_tracker/pipeline.py +86 -0
- pal/llm_scheduler/__init__.py +11 -0
- pal/llm_scheduler/api.py +137 -0
- pal/llm_scheduler/code.py +28 -0
- pal/llm_scheduler/core.py +92 -0
- pal/llm_scheduler/local.py +215 -0
- pal/llm_scheduler/pipeline.py +42 -0
- pal/llm_scheduler/types.py +92 -0
- pal/llm_wrapper/__init__.py +34 -0
- pal/llm_wrapper/adapter.py +316 -0
- pal/llm_wrapper/components/__init__.py +23 -0
- pal/llm_wrapper/components/adapter_types.py +90 -0
- pal/llm_wrapper/components/base.py +195 -0
- pal/llm_wrapper/components/features.py +104 -0
- pal/llm_wrapper/components/litellm_wrapper.py +215 -0
- pal/llm_wrapper/components/openrouter_wrapper.py +114 -0
- pal/llm_wrapper/modes.py +73 -0
- pal/llm_wrapper/pipeline.py +61 -0
- pal/llm_wrapper/utils/__init__.py +4 -0
- pal/llm_wrapper/utils/registry.py +49 -0
- pal/llm_wrapper/utils/token_counting.py +10 -0
- pal/local_llm/__init__.py +5 -0
- pal/local_llm/components/__init__.py +0 -0
- pal/local_llm/components/huggingface_local.py +217 -0
- pal/local_llm/components/local_llm.py +60 -0
- pal/logging_setup.py +18 -0
- pal/memory/__init__.py +7 -0
- pal/memory/components/__init__.py +6 -0
- pal/memory/components/lru_k.py +56 -0
- pal/memory/components/manager.py +98 -0
- pal/memory/components/note.py +54 -0
- pal/memory/components/storage_manager.py +60 -0
- pal/memory/pipeline.py +122 -0
- pal/memory/utils/__init__.py +3 -0
- pal/memory/utils/keyword_overlap.py +3 -0
- pal/memory_tracker/__init__.py +3 -0
- pal/memory_tracker/components/__init__.py +0 -0
- pal/memory_tracker/components/process.py +56 -0
- pal/memory_tracker/pipeline.py +44 -0
- pal/metrics/__init__.py +4 -0
- pal/metrics/components/__init__.py +4 -0
- pal/metrics/components/collector.py +140 -0
- pal/metrics/components/exporter.py +28 -0
- pal/metrics/pipeline.py +49 -0
- pal/metrics/utils/__init__.py +2 -0
- pal/notes_manager/__init__.py +5 -0
- pal/notes_manager/components/__init__.py +0 -0
- pal/notes_manager/components/privacy.py +14 -0
- pal/notes_manager/components/recall.py +31 -0
- pal/notes_manager/components/tidy.py +28 -0
- pal/notes_manager/pipeline.py +138 -0
- pal/orchestrator/__init__.py +26 -0
- pal/orchestrator/components/__init__.py +26 -0
- pal/orchestrator/components/agent.py +105 -0
- pal/orchestrator/components/cgroup_manager.py +209 -0
- pal/orchestrator/components/context_interrupt.py +220 -0
- pal/orchestrator/components/limits.py +9 -0
- pal/orchestrator/components/scheduler.py +69 -0
- pal/orchestrator/components/syscall_scheduler.py +200 -0
- pal/orchestrator/pipeline.py +228 -0
- pal/orchestrator/utils/__init__.py +4 -0
- pal/orchestrator/utils/device.py +164 -0
- pal/orchestrator/utils/protocol.py +32 -0
- pal/os_kernel/__init__.py +5 -0
- pal/os_kernel/filesystem.py +80 -0
- pal/os_kernel/network.py +146 -0
- pal/os_kernel/pipeline.py +175 -0
- pal/os_kernel/sysinfo.py +36 -0
- pal/paths.py +59 -0
- pal/pipeline.py +177 -0
- pal/py.typed +0 -0
- pal/redact.py +19 -0
- pal/sdk/__init__.py +33 -0
- pal/sdk/adapters.py +43 -0
- pal/sdk/agent.py +99 -0
- pal/sdk/components/__init__.py +14 -0
- pal/sdk/components/autogen_adapter.py +65 -0
- pal/sdk/components/autogen_client.py +98 -0
- pal/sdk/components/framework_adapter_base.py +69 -0
- pal/sdk/components/metagpt_adapter.py +46 -0
- pal/sdk/components/open_interpreter_adapter.py +47 -0
- pal/sdk/components/tool_catalog.py +44 -0
- pal/sdk/os_api.py +84 -0
- pal/sdk/patterns.py +129 -0
- pal/sdk/pipeline.py +240 -0
- pal/sdk/queries.py +68 -0
- pal/sdk/savers.py +88 -0
- pal/sdk/utils/__init__.py +2 -0
- pal/storage_manager/__init__.py +17 -0
- pal/storage_manager/components/__init__.py +19 -0
- pal/storage_manager/components/base.py +50 -0
- pal/storage_manager/components/local_file_backend.py +84 -0
- pal/storage_manager/components/storage_queue.py +91 -0
- pal/storage_manager/components/vector_store_backend.py +122 -0
- pal/storage_manager/pipeline.py +153 -0
- pal/storage_manager/utils/__init__.py +4 -0
- pal/storage_manager/utils/memory_adapter.py +60 -0
- pal/storage_manager/utils/sdk.py +93 -0
- pal/studio/__init__.py +0 -0
- pal/studio/builder.py +166 -0
- pal/studio/catalog.py +90 -0
- pal/studio/checks.py +129 -0
- pal/studio/clients.py +6 -0
- pal/studio/editor.py +137 -0
- pal/studio/files.py +6 -0
- pal/studio/models.py +6 -0
- pal/studio/plan.py +149 -0
- pal/studio/planner.py +205 -0
- pal/studio/policy.py +69 -0
- pal/studio/routes.py +254 -0
- pal/studio/runs.py +210 -0
- pal/task_router/__init__.py +11 -0
- pal/task_router/components/__init__.py +0 -0
- pal/task_router/components/answerer.py +37 -0
- pal/task_router/components/base.py +34 -0
- pal/task_router/components/classifier.py +40 -0
- pal/task_router/components/gates.py +23 -0
- pal/task_router/pipeline.py +61 -0
- pal/task_router/utils/__init__.py +1 -0
- pal/terminal/__init__.py +1 -0
- pal/terminal/backend.py +141 -0
- pal/terminal/bench.py +173 -0
- pal/terminal/cli.py +826 -0
- pal/terminal/clients.py +170 -0
- pal/terminal/commands.py +401 -0
- pal/terminal/decide.py +242 -0
- pal/terminal/files.py +55 -0
- pal/terminal/flow_cli.py +439 -0
- pal/terminal/hardware.py +199 -0
- pal/terminal/history.py +88 -0
- pal/terminal/keys.py +83 -0
- pal/terminal/local_models.py +261 -0
- pal/terminal/logo.py +117 -0
- pal/terminal/models.py +99 -0
- pal/terminal/policy.py +54 -0
- pal/terminal/project.py +413 -0
- pal/terminal/review.py +65 -0
- pal/terminal/security.py +239 -0
- pal/terminal/session.py +503 -0
- pal/terminal/setup.py +354 -0
- pal/terminal/syntax.py +98 -0
- pal/terminal/theme.py +37 -0
- pal/terminal/ui.py +647 -0
- pal/terminal/voice.py +137 -0
- pal/terminal/workflow.py +624 -0
- pal/token_tracker/__init__.py +3 -0
- pal/token_tracker/pipeline.py +54 -0
- pal/tokens.py +21 -0
- pal/tool_catalog.py +107 -0
- pal/tool_manager/__init__.py +17 -0
- pal/tool_manager/builtin.py +82 -0
- pal/tool_manager/components/__init__.py +19 -0
- pal/tool_manager/components/base.py +53 -0
- pal/tool_manager/components/conflict_resolver.py +27 -0
- pal/tool_manager/components/param_validator.py +41 -0
- pal/tool_manager/components/tool_call_queue.py +111 -0
- pal/tool_manager/components/tool_registry.py +60 -0
- pal/tool_manager/pipeline.py +80 -0
- pal/tool_manager/utils/__init__.py +3 -0
- pal/web/__init__.py +3 -0
- pal/web/app.py +276 -0
- pal/web/static/editor.js +219 -0
- pal/web/static/fonts/LICENSE-Inter.txt +3 -0
- pal/web/static/fonts/inter-400.woff2 +0 -0
- pal/web/static/fonts/inter-500.woff2 +0 -0
- pal/web/static/fonts/inter-600.woff2 +0 -0
- pal/web/static/fonts/inter-700.woff2 +0 -0
- pal/web/static/inter.css +5 -0
- pal/web/static/pal-logo-256.png +0 -0
- pal/web/static/pal-logo-32.png +0 -0
- pal/web/static/pal-logo-64.png +0 -0
- pal/web/static/studio.css +306 -0
- pal/web/static/studio.js +533 -0
- pal/web/static/voice.js +76 -0
- pal/web/templates/index.html +368 -0
- pal/web/templates/studio.html +120 -0
- pal_code-0.1.0.dist-info/METADATA +379 -0
- pal_code-0.1.0.dist-info/RECORD +282 -0
- pal_code-0.1.0.dist-info/WHEEL +5 -0
- pal_code-0.1.0.dist-info/entry_points.txt +2 -0
- pal_code-0.1.0.dist-info/licenses/LICENSE +21 -0
- pal_code-0.1.0.dist-info/top_level.txt +1 -0
pal/terminal/voice.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Talk instead of typing: record from the microphone, turn it into text, and put the text in the input box to edit and send.
|
|
2
|
+
|
|
3
|
+
In the terminal: Ctrl+Space (or /voice). In the web chat: the microphone button. The words are never sent on their own: they land in the box.
|
|
4
|
+
|
|
5
|
+
Turning speech into text, in this order:
|
|
6
|
+
1. on this computer with faster-whisper, when it is installed (pip install "pal-code[voice]"): nothing leaves the computer;
|
|
7
|
+
2. else OpenAI's transcription (OPENAI_API_KEY) or Groq's Whisper (GROQ_API_KEY), with your key;
|
|
8
|
+
3. else Pal says what to install.
|
|
9
|
+
Recording in the terminal needs sounddevice (in the same extra). Dictation apps such as Wispr Flow need none of this: they type into the
|
|
10
|
+
box like a keyboard, in the terminal and in the browser.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import io
|
|
15
|
+
import os
|
|
16
|
+
import tempfile
|
|
17
|
+
import threading
|
|
18
|
+
import wave
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
RATE = 16_000
|
|
24
|
+
MAX_SECONDS = 120
|
|
25
|
+
LOCAL_MODEL = os.environ.get("PAL_WHISPER_MODEL", "base")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class VoiceUnavailable(RuntimeError):
|
|
29
|
+
"""Recording or transcription cannot work here; the message says what to install or set."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Transcript:
|
|
34
|
+
text: str
|
|
35
|
+
engine: str # "local whisper (base)", "openai", "groq"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def can_record() -> bool:
|
|
39
|
+
try:
|
|
40
|
+
import sounddevice # noqa: F401
|
|
41
|
+
return True
|
|
42
|
+
except Exception: # noqa: BLE001 (not installed, or PortAudio missing)
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def engines() -> list[str]:
|
|
47
|
+
"""The transcription engines available now, best first."""
|
|
48
|
+
out = []
|
|
49
|
+
try:
|
|
50
|
+
import faster_whisper # noqa: F401
|
|
51
|
+
out.append("local")
|
|
52
|
+
except Exception: # noqa: BLE001
|
|
53
|
+
pass
|
|
54
|
+
if os.environ.get("OPENAI_API_KEY"):
|
|
55
|
+
out.append("openai")
|
|
56
|
+
if os.environ.get("GROQ_API_KEY"):
|
|
57
|
+
out.append("groq")
|
|
58
|
+
return out
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def wav_bytes(samples, rate: int = RATE) -> bytes:
|
|
62
|
+
"""16-bit mono WAV from float samples in [-1, 1] (a numpy array or a list)."""
|
|
63
|
+
import array
|
|
64
|
+
pcm = array.array("h", (max(-32768, min(32767, int(s * 32767))) for s in samples))
|
|
65
|
+
buf = io.BytesIO()
|
|
66
|
+
with wave.open(buf, "wb") as w:
|
|
67
|
+
w.setnchannels(1)
|
|
68
|
+
w.setsampwidth(2)
|
|
69
|
+
w.setframerate(rate)
|
|
70
|
+
w.writeframes(pcm.tobytes())
|
|
71
|
+
return buf.getvalue()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def record(stop: threading.Event, max_seconds: int = MAX_SECONDS) -> bytes:
|
|
75
|
+
"""Record until `stop` is set (or max_seconds), as WAV bytes."""
|
|
76
|
+
try:
|
|
77
|
+
import numpy as np
|
|
78
|
+
import sounddevice as sd
|
|
79
|
+
except Exception as e: # noqa: BLE001
|
|
80
|
+
raise VoiceUnavailable('recording needs sounddevice: pip install "pal-code[voice]" (Linux also: sudo apt install libportaudio2)') from e
|
|
81
|
+
chunks: list = []
|
|
82
|
+
|
|
83
|
+
def take(data, frames, time_info, status) -> None:
|
|
84
|
+
chunks.append(data.copy())
|
|
85
|
+
try:
|
|
86
|
+
with sd.InputStream(samplerate=RATE, channels=1, dtype="float32", callback=take):
|
|
87
|
+
stop.wait(max_seconds)
|
|
88
|
+
except Exception as e: # noqa: BLE001 (no microphone, or permission denied)
|
|
89
|
+
raise VoiceUnavailable(f"could not use the microphone: {e}") from e
|
|
90
|
+
audio = np.concatenate(chunks)[:, 0] if chunks else np.zeros(0, dtype="float32")
|
|
91
|
+
return wav_bytes(audio.tolist())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
_LOCAL: dict = {}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _local(audio: bytes, suffix: str) -> str:
|
|
98
|
+
from faster_whisper import WhisperModel
|
|
99
|
+
if LOCAL_MODEL not in _LOCAL:
|
|
100
|
+
_LOCAL[LOCAL_MODEL] = WhisperModel(LOCAL_MODEL, device="auto", compute_type="auto")
|
|
101
|
+
with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as f:
|
|
102
|
+
f.write(audio)
|
|
103
|
+
name = f.name
|
|
104
|
+
try:
|
|
105
|
+
segments, _ = _LOCAL[LOCAL_MODEL].transcribe(name, vad_filter=True)
|
|
106
|
+
return " ".join(s.text.strip() for s in segments).strip()
|
|
107
|
+
finally:
|
|
108
|
+
Path(name).unlink(missing_ok=True)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _api(audio: bytes, suffix: str, engine: str) -> str:
|
|
112
|
+
from pal.llm_wrapper.components.litellm_wrapper import _import_litellm
|
|
113
|
+
ll = _import_litellm()
|
|
114
|
+
model = {"openai": "whisper-1", "groq": "groq/whisper-large-v3"}[engine]
|
|
115
|
+
key = os.environ.get("OPENAI_API_KEY" if engine == "openai" else "GROQ_API_KEY")
|
|
116
|
+
f = io.BytesIO(audio)
|
|
117
|
+
f.name = "speech" + suffix
|
|
118
|
+
r = ll.transcription(model=model, file=f, api_key=key)
|
|
119
|
+
return str(getattr(r, "text", "") or (r.get("text", "") if isinstance(r, dict) else "")).strip()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def transcribe(audio: bytes, suffix: str = ".wav", prefer: list[str] | None = None,
|
|
123
|
+
runners: dict[str, Callable[[bytes, str], str]] | None = None) -> Transcript:
|
|
124
|
+
"""Speech to text with the best engine available. Raises VoiceUnavailable when there is none (or every one failed)."""
|
|
125
|
+
order = prefer or engines()
|
|
126
|
+
if not order:
|
|
127
|
+
raise VoiceUnavailable('no speech-to-text here: pip install "pal-code[voice]" (runs on this computer), or set OPENAI_API_KEY or GROQ_API_KEY')
|
|
128
|
+
runners = runners or {"local": _local, "openai": lambda a, s: _api(a, s, "openai"), "groq": lambda a, s: _api(a, s, "groq")}
|
|
129
|
+
problems = []
|
|
130
|
+
for engine in order:
|
|
131
|
+
try:
|
|
132
|
+
text = runners[engine](audio, suffix)
|
|
133
|
+
except Exception as e: # noqa: BLE001 (try the next engine)
|
|
134
|
+
problems.append(f"{engine}: {str(e)[:120]}")
|
|
135
|
+
continue
|
|
136
|
+
return Transcript(text, f"local whisper ({LOCAL_MODEL})" if engine == "local" else engine)
|
|
137
|
+
raise VoiceUnavailable("speech-to-text failed: " + "; ".join(problems))
|