pal-code 0.1.2__tar.gz → 0.1.3__tar.gz
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_code-0.1.2 → pal_code-0.1.3}/PKG-INFO +5 -5
- {pal_code-0.1.2 → pal_code-0.1.3}/pyproject.toml +6 -3
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/__init__.py +1 -1
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/cli.py +17 -15
- pal_code-0.1.3/src/pal/llm_wrapper/__init__.py +34 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/cli.py +208 -67
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/setup.py +26 -1
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/ui.py +64 -34
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/voice.py +3 -3
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/workflow.py +3 -3
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/voice.js +1 -1
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/templates/index.html +4 -12
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal_code.egg-info/PKG-INFO +5 -5
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal_code.egg-info/requires.txt +4 -4
- pal_code-0.1.2/src/pal/llm_wrapper/__init__.py +0 -34
- {pal_code-0.1.2 → pal_code-0.1.3}/LICENSE +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/README.md +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/setup.cfg +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/__main__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/components/access_control.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/components/intervention.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/components/privilege_registry.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/access_manager/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agent_constraints/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agent_constraints/components/AGENT_OUTPUT.md +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agent_constraints/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agent_constraints/components/constraints.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agent_constraints/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agent_constraints/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agents/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agents/coding.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/agents/docs.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/demo.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/providers.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/service.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/services/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/services/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/services/data.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/services/media.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/services/web.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/sessions.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/tools.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/chat/uploads.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/adverbs.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/hedges.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/interjections.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/minifier.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/output_compressor.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/pleonasms.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/punctuations.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/components/stopwords.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/history.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/utils/negations.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/utils/nltk_setup.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/utils/pronouns.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/utils/proselint_setup.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/compression/utils/spacy_setup.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/config/default.yaml +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/config.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/chat_context.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/assembler.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/context_cache.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/kv_bridge.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/pager.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/simple_context_manager.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/components/turns.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/utils/hashing.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/utils/relevance.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_manager/utils/storage.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/context_window.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/documents/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/documents/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/documents/components/chunker.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/documents/components/index.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/documents/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/errors.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/components/pandoc_converter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/components/pdf_converter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/components/pptx_converter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/components/xlsx_converter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_converters/workspace.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/INPUT_RULES.md +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/components/methods.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/components/verifier.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/input_savers/utils/guards.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/components/builder.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/components/retriever.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/components/workspace.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/knowledge_graph/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/latency_tracker/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/latency_tracker/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/api.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/code.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/core.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/local.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_scheduler/types.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/adapter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/components/adapter_types.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/components/features.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/components/litellm_wrapper.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/components/openrouter_wrapper.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/modes.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/utils/registry.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/llm_wrapper/utils/token_counting.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/local_llm/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/local_llm/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/local_llm/components/huggingface_local.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/local_llm/components/local_llm.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/logging_setup.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/components/lru_k.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/components/manager.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/components/note.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/components/storage_manager.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory/utils/keyword_overlap.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory_tracker/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory_tracker/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory_tracker/components/process.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/memory_tracker/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/metrics/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/metrics/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/metrics/components/collector.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/metrics/components/exporter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/metrics/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/metrics/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/notes_manager/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/notes_manager/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/notes_manager/components/privacy.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/notes_manager/components/recall.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/notes_manager/components/tidy.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/notes_manager/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/agent.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/cgroup_manager.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/context_interrupt.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/limits.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/scheduler.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/components/syscall_scheduler.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/utils/device.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/orchestrator/utils/protocol.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/os_kernel/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/os_kernel/filesystem.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/os_kernel/network.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/os_kernel/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/os_kernel/sysinfo.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/paths.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/py.typed +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/redact.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/adapters.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/agent.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/autogen_adapter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/autogen_client.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/framework_adapter_base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/metagpt_adapter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/open_interpreter_adapter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/components/tool_catalog.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/os_api.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/patterns.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/queries.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/savers.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/sdk/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/components/local_file_backend.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/components/storage_queue.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/components/vector_store_backend.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/utils/memory_adapter.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/storage_manager/utils/sdk.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/builder.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/catalog.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/checks.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/clients.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/editor.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/files.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/models.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/plan.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/planner.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/policy.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/routes.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/studio/runs.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/components/answerer.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/components/classifier.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/components/gates.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/task_router/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/backend.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/bench.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/clients.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/commands.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/decide.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/files.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/flow_cli.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/hardware.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/history.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/keys.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/local_models.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/logo.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/models.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/policy.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/project.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/review.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/security.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/session.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/syntax.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/terminal/theme.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/token_tracker/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/token_tracker/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tokens.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_catalog.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/builtin.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/components/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/components/base.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/components/conflict_resolver.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/components/param_validator.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/components/tool_call_queue.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/components/tool_registry.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/pipeline.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/tool_manager/utils/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/__init__.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/app.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/editor.js +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/fonts/LICENSE-Inter.txt +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-400.woff2 +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-500.woff2 +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-600.woff2 +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/fonts/inter-700.woff2 +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/inter.css +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/pal-logo-256.png +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/pal-logo-32.png +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/pal-logo-64.png +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/studio.css +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/static/studio.js +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal/web/templates/studio.html +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal_code.egg-info/SOURCES.txt +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal_code.egg-info/dependency_links.txt +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal_code.egg-info/entry_points.txt +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/src/pal_code.egg-info/top_level.txt +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_cli.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_config.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_lockfile.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_paths.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_redact.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_run_script.py +0 -0
- {pal_code-0.1.2 → pal_code-0.1.3}/tests/test_tool_catalog.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pal-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Pal: build and plan with AI in your terminal (pal cli) or browser (pal web), on a layer that cuts tokens and latency.
|
|
5
5
|
Author: Vrushal More
|
|
6
6
|
License: MIT
|
|
@@ -26,6 +26,9 @@ Requires-Dist: graphifyy>=0.9
|
|
|
26
26
|
Requires-Dist: pypdf>=4.0
|
|
27
27
|
Requires-Dist: python-pptx>=0.6.23
|
|
28
28
|
Requires-Dist: openpyxl>=3.1
|
|
29
|
+
Requires-Dist: sounddevice>=0.4
|
|
30
|
+
Requires-Dist: numpy>=1.24
|
|
31
|
+
Requires-Dist: faster-whisper>=1.0
|
|
29
32
|
Provides-Extra: documents
|
|
30
33
|
Requires-Dist: pymupdf4llm>=0.0.17; extra == "documents"
|
|
31
34
|
Provides-Extra: graph
|
|
@@ -42,11 +45,8 @@ Provides-Extra: inprocess
|
|
|
42
45
|
Requires-Dist: torch; extra == "inprocess"
|
|
43
46
|
Requires-Dist: transformers; extra == "inprocess"
|
|
44
47
|
Provides-Extra: voice
|
|
45
|
-
Requires-Dist: sounddevice>=0.4; extra == "voice"
|
|
46
|
-
Requires-Dist: numpy>=1.24; extra == "voice"
|
|
47
|
-
Requires-Dist: faster-whisper>=1.0; extra == "voice"
|
|
48
48
|
Provides-Extra: full
|
|
49
|
-
Requires-Dist: pal-code[documents
|
|
49
|
+
Requires-Dist: pal-code[documents]; extra == "full"
|
|
50
50
|
Provides-Extra: dev
|
|
51
51
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
52
52
|
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pal-code"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Pal: build and plan with AI in your terminal (pal cli) or browser (pal web), on a layer that cuts tokens and latency."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -32,6 +32,9 @@ dependencies = [
|
|
|
32
32
|
"pypdf>=4.0", # PDF -> markdown
|
|
33
33
|
"python-pptx>=0.6.23", # PowerPoint -> markdown
|
|
34
34
|
"openpyxl>=3.1", # Excel -> markdown
|
|
35
|
+
"sounddevice>=0.4", # the mic: recording (PortAudio comes with it on Windows and macOS)
|
|
36
|
+
"numpy>=1.24",
|
|
37
|
+
"faster-whisper>=1.0", # speech to text on this computer (Whisper); nothing leaves the computer
|
|
35
38
|
]
|
|
36
39
|
|
|
37
40
|
[project.optional-dependencies]
|
|
@@ -42,8 +45,8 @@ litellm = [] # kept so older install lines still work: litel
|
|
|
42
45
|
vector = ["chromadb"]
|
|
43
46
|
embeddings = ["sentence-transformers"]
|
|
44
47
|
inprocess = ["torch", "transformers"]
|
|
45
|
-
voice = [
|
|
46
|
-
full = ["pal-code[documents
|
|
48
|
+
voice = [] # kept so older install lines still work: the mic is installed by default now
|
|
49
|
+
full = ["pal-code[documents]"]
|
|
47
50
|
dev = ["pytest>=8", "ruff>=0.5", "mypy>=1.10", "types-requests", "types-PyYAML"]
|
|
48
51
|
|
|
49
52
|
[project.scripts]
|
|
@@ -212,7 +212,8 @@ def load_dotenv(path: str | os.PathLike | None = None, env=None) -> list[str]:
|
|
|
212
212
|
return loaded
|
|
213
213
|
|
|
214
214
|
|
|
215
|
-
def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph=None, documents: list | None = None, project_dir=None
|
|
215
|
+
def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph=None, documents: list | None = None, project_dir=None,
|
|
216
|
+
background=None) -> int:
|
|
216
217
|
from pal.chat import ChatService
|
|
217
218
|
from pal.web import create_app
|
|
218
219
|
|
|
@@ -222,6 +223,9 @@ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph
|
|
|
222
223
|
if documents:
|
|
223
224
|
service.preload_docs = list(documents)
|
|
224
225
|
app = create_app(service, studio={"project_dir": project_dir}) if project_dir else create_app(service)
|
|
226
|
+
if background is not None: # pal web: the folder's setup runs while the page is already usable
|
|
227
|
+
import threading as _bg
|
|
228
|
+
_bg.Thread(target=background, args=(service,), daemon=True, name="pal-web-setup").start()
|
|
225
229
|
if s.host not in _LOOPBACK:
|
|
226
230
|
print(f"WARNING: listening on {s.host}. Pal has no login: anyone who can reach this port can use "
|
|
227
231
|
"your API keys' quota and read stored memory. Put it behind an authenticating reverse proxy.",
|
|
@@ -229,23 +233,21 @@ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph
|
|
|
229
233
|
shown = "localhost" if s.host in ("0.0.0.0", "::") else s.host # noqa: S104
|
|
230
234
|
url = f"http://{shown}:{s.port}" + ("/" + page.strip("/") if page else "")
|
|
231
235
|
print(f"Pal {__version__} on {url} (Ctrl+C to stop)")
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
from pal.context_window import context_window
|
|
244
|
-
_t.Thread(target=lambda: context_window("openai", "gpt-4o"), daemon=True).start() # loads LiteLLM's model list now, not on the first page load
|
|
236
|
+
if background is None: # pal serve: say what is set up; pal web says nothing more and starts at once
|
|
237
|
+
from pal.chat.providers import API_PROVIDERS
|
|
238
|
+
keys = [p for p, var in API_PROVIDERS.items() if os.environ.get(var)]
|
|
239
|
+
print("API key found for: " + ", ".join(keys) if keys else
|
|
240
|
+
"No API key in the environment: paste one in the page (API key menu), or set e.g. GEMINI_API_KEY.")
|
|
241
|
+
status = service.local_status()
|
|
242
|
+
if status["ok"] is False:
|
|
243
|
+
print(f"WARNING: local model: {status['detail']}. Input reduction and local answers are off until it runs; "
|
|
244
|
+
"the app still works and sends everything to the API model.", file=sys.stderr)
|
|
245
|
+
else:
|
|
246
|
+
print(f"Local model: {status['detail']}")
|
|
245
247
|
if open_browser:
|
|
246
248
|
import threading
|
|
247
249
|
import webbrowser
|
|
248
|
-
threading.Timer(
|
|
250
|
+
threading.Timer(0.3, lambda: webbrowser.open(url)).start()
|
|
249
251
|
try:
|
|
250
252
|
try:
|
|
251
253
|
from waitress import serve
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""The model layer. Its names are loaded on first use (PEP 562), so importing a light submodule such as pal.llm_wrapper.modes (the provider
|
|
2
|
+
names and key variables) does not load the whole model stack: pal cli draws its first screen before any of it is needed."""
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
_WHERE = {
|
|
6
|
+
"LLMAdapter": "pal.llm_wrapper.adapter",
|
|
7
|
+
"LLMAdapterResponse": "pal.llm_wrapper.components.adapter_types",
|
|
8
|
+
"LLMAdapterSyscall": "pal.llm_wrapper.components.adapter_types",
|
|
9
|
+
"LLMConfig": "pal.llm_wrapper.components.adapter_types",
|
|
10
|
+
"RouterStrategy": "pal.llm_wrapper.components.adapter_types",
|
|
11
|
+
"DeploymentType": "pal.llm_wrapper.components.base",
|
|
12
|
+
"LLMResponse": "pal.llm_wrapper.components.base",
|
|
13
|
+
"LLMSyscall": "pal.llm_wrapper.components.base",
|
|
14
|
+
"LLMWrapper": "pal.llm_wrapper.components.base",
|
|
15
|
+
"WrapperCapabilities": "pal.llm_wrapper.components.base",
|
|
16
|
+
"WrapperConfig": "pal.llm_wrapper.components.base",
|
|
17
|
+
"LLMWrapperPipeline": "pal.llm_wrapper.pipeline",
|
|
18
|
+
"available_backends": "pal.llm_wrapper.utils.registry",
|
|
19
|
+
"build_wrapper": "pal.llm_wrapper.utils.registry",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
__all__ = list(_WHERE)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def __getattr__(name: str):
|
|
26
|
+
if name in _WHERE:
|
|
27
|
+
value = getattr(import_module(_WHERE[name]), name)
|
|
28
|
+
globals()[name] = value
|
|
29
|
+
return value
|
|
30
|
+
raise AttributeError(f"module 'pal.llm_wrapper' has no attribute {name!r}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def __dir__():
|
|
34
|
+
return sorted(set(globals()) | set(_WHERE))
|
|
@@ -239,36 +239,43 @@ def prepare_folder(args, console, core_factory: Callable | None = None, choose:
|
|
|
239
239
|
|
|
240
240
|
|
|
241
241
|
def web(args) -> int:
|
|
242
|
-
"""Start the web interface on this computer and open it in the default browser. It listens on 127.0.0.1 only, unless the settings
|
|
243
|
-
The helper
|
|
242
|
+
"""Start the web interface on this computer and open it in the default browser, at once. It listens on 127.0.0.1 only, unless the settings
|
|
243
|
+
say otherwise. The setup (Ollama, the helper, this folder's documents, map and graph) runs in the background; the chat gets the folder's
|
|
244
|
+
documents as soon as they are ready."""
|
|
244
245
|
from pal.cli import _serve, load_dotenv
|
|
245
246
|
from pal.config import load_settings
|
|
246
247
|
from pal.errors import ConfigError
|
|
248
|
+
from pal.terminal.project import Project, ProjectGraph
|
|
247
249
|
load_dotenv()
|
|
248
|
-
console = ui.make_console()
|
|
249
|
-
local, project = prepare_folder(args, console)
|
|
250
|
-
if local:
|
|
251
|
-
ui.say(console, Text(f"Local LLM: {local}", style=theme.MUTED))
|
|
252
|
-
if project is not None:
|
|
253
|
-
r = project.report
|
|
254
|
-
ui.say(console, Text(f"This folder: {r.code_files} files \u00B7 {len(r.converted)} documents as markdown \u00B7 code graph {r.graph}", style=theme.MUTED))
|
|
255
250
|
overrides: dict = {"server": {"port": args.port}} if args.port else {}
|
|
256
251
|
helper = backend.load_prefs().get("helper_model")
|
|
257
|
-
|
|
258
|
-
overrides["local_llm"] = {"enabled": False} # no GPU: API models only, no local helper jobs
|
|
259
|
-
elif helper:
|
|
260
|
-
overrides["local_llm"] = {"backend": "ollama", "model": helper}
|
|
252
|
+
overrides["local_llm"] = {"backend": "ollama", "model": helper} if helper else {"enabled": False} # no helper yet: API models only
|
|
261
253
|
try:
|
|
262
254
|
settings = load_settings(overrides=overrides or None)
|
|
263
255
|
except ConfigError as e:
|
|
264
256
|
print(f"config error: {e}", file=sys.stderr)
|
|
265
257
|
return 2
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
258
|
+
folder = Path(getattr(args, "dir", ".") or ".").resolve()
|
|
259
|
+
project = None if os.environ.get("PAL_SKIP_PROJECT") else Project(folder)
|
|
260
|
+
graph = ProjectGraph(project.root / "graph", settings.knowledge_graph) if project is not None else None
|
|
261
|
+
|
|
262
|
+
def in_background(service) -> None:
|
|
263
|
+
import io
|
|
264
|
+
|
|
265
|
+
from rich.console import Console
|
|
266
|
+
from pal.terminal.setup import ensure_local
|
|
267
|
+
try:
|
|
268
|
+
host = args.host or backend.DEFAULT_HOST
|
|
269
|
+
hw = backend.hardware()
|
|
270
|
+
ensure_local(Console(file=io.StringIO()), hw, host, interactive=False, choose=False)
|
|
271
|
+
background_helper(hw, backend.local_info(host))
|
|
272
|
+
if project is not None:
|
|
273
|
+
project.prepare()
|
|
274
|
+
service.preload_docs = list(project.documents()) # new chats get the folder's documents and map
|
|
275
|
+
except Exception: # noqa: BLE001 (the web interface works without the folder's setup)
|
|
276
|
+
pass
|
|
270
277
|
return _serve(settings, open_browser=not args.no_open, page="studio" if args.page == "studio" else "", graph=graph,
|
|
271
|
-
|
|
278
|
+
project_dir=folder, background=in_background)
|
|
272
279
|
|
|
273
280
|
|
|
274
281
|
def _utf8() -> None:
|
|
@@ -284,7 +291,7 @@ NO_GPU = ("No GPU on this computer, so Pal uses an API model. Choose one with:\n
|
|
|
284
291
|
" browse models: pal models --free")
|
|
285
292
|
|
|
286
293
|
|
|
287
|
-
def start(args, core_factory: Callable | None = None, project=None) -> PalSession | None:
|
|
294
|
+
def start(args, core_factory: Callable | None = None, project=None, prompt: bool = True) -> PalSession | None:
|
|
288
295
|
"""The session for these arguments, or None (after saying why) when there is no model to talk to.
|
|
289
296
|
On a computer without a GPU there is no local model: the session uses an API model and Pal's local helper jobs are off."""
|
|
290
297
|
console = ui.make_console()
|
|
@@ -292,9 +299,11 @@ def start(args, core_factory: Callable | None = None, project=None) -> PalSessio
|
|
|
292
299
|
prefs = backend.load_prefs()
|
|
293
300
|
no_gpu = core_factory is None and api_only(backend.hardware())
|
|
294
301
|
if no_gpu:
|
|
295
|
-
return _start_api_only(args, prefs, console, project)
|
|
302
|
+
return _start_api_only(args, prefs, console, project, prompt)
|
|
296
303
|
want = args.provider or prefs.get("provider", "")
|
|
297
304
|
if want in backend.PROVIDER_KEYS and not os.environ.get(backend.PROVIDER_KEYS[want]) and core_factory is None and sys.stdin.isatty():
|
|
305
|
+
if not prompt:
|
|
306
|
+
return None # the key is asked for in the foreground
|
|
298
307
|
ask_key(console, want) # the main model is an API model: its key (never saved) for this session
|
|
299
308
|
info = backend.local_info(host)
|
|
300
309
|
if args.provider and args.provider != backend.LOCAL:
|
|
@@ -338,7 +347,7 @@ def ask_key(console, provider: str) -> bool:
|
|
|
338
347
|
return bool(key)
|
|
339
348
|
|
|
340
349
|
|
|
341
|
-
def _start_api_only(args, prefs: dict, console, project=None) -> PalSession | None:
|
|
350
|
+
def _start_api_only(args, prefs: dict, console, project=None, prompt: bool = True) -> PalSession | None:
|
|
342
351
|
from pal.terminal.setup import ask_api
|
|
343
352
|
keyed = lambda p: p in backend.OPEN_PROVIDERS or (p in backend.PROVIDER_KEYS and bool(os.environ.get(backend.PROVIDER_KEYS[p]))) # noqa: E731
|
|
344
353
|
if args.provider == backend.LOCAL:
|
|
@@ -347,6 +356,8 @@ def _start_api_only(args, prefs: dict, console, project=None) -> PalSession | No
|
|
|
347
356
|
if provider == backend.LOCAL:
|
|
348
357
|
provider, model = "", ""
|
|
349
358
|
if not (provider and model and keyed(provider)):
|
|
359
|
+
if not prompt:
|
|
360
|
+
return None
|
|
350
361
|
if sys.stdin.isatty() and sys.stdout.isatty():
|
|
351
362
|
console.print(Text("\n No GPU on this computer, so Pal uses an API model.\n", style=f"bold {theme.RED_HI}"))
|
|
352
363
|
chosen = ask_api(console, backend.hardware().system)
|
|
@@ -726,12 +737,12 @@ def voice_flow(console) -> str:
|
|
|
726
737
|
|
|
727
738
|
from pal.terminal import voice
|
|
728
739
|
if not voice.can_record():
|
|
729
|
-
ui.say(console, Text(
|
|
730
|
-
"
|
|
740
|
+
ui.say(console, Text("The mic is not available here: no microphone was found, or (on Linux) PortAudio is missing: sudo apt install libportaudio2. "
|
|
741
|
+
"Windows voice typing (Win+H) and apps such as Wispr Flow type into the box too.", style=theme.MUTED))
|
|
731
742
|
console.print()
|
|
732
743
|
return ""
|
|
733
744
|
if not voice.engines():
|
|
734
|
-
ui.say(console, Text(
|
|
745
|
+
ui.say(console, Text("Speech to text is not available: reinstall Pal (pip install -U pal-code), or set OPENAI_API_KEY or GROQ_API_KEY.", style=theme.MUTED))
|
|
735
746
|
console.print()
|
|
736
747
|
return ""
|
|
737
748
|
stop, out, err = threading.Event(), {}, {}
|
|
@@ -757,7 +768,9 @@ def voice_flow(console) -> str:
|
|
|
757
768
|
ui.say(console, Text(err.get("e", "Nothing was recorded."), style=theme.RED_HI))
|
|
758
769
|
return ""
|
|
759
770
|
try:
|
|
760
|
-
|
|
771
|
+
first_time = "local" in voice.engines() and not voice._LOCAL
|
|
772
|
+
label = "Turning speech into text (the first time, the speech model is downloaded once, about 150 MB)…" if first_time else "Turning speech into text…"
|
|
773
|
+
with console.status(Text(label, style=theme.MUTED), spinner="dots", spinner_style=theme.RED):
|
|
761
774
|
heard = voice.transcribe(out["wav"])
|
|
762
775
|
except voice.VoiceUnavailable as e:
|
|
763
776
|
ui.say(console, Text(str(e), style=theme.RED_HI))
|
|
@@ -766,60 +779,186 @@ def voice_flow(console) -> str:
|
|
|
766
779
|
return heard.text
|
|
767
780
|
|
|
768
781
|
|
|
769
|
-
def
|
|
770
|
-
"""
|
|
771
|
-
return (core_factory is None and
|
|
772
|
-
and not
|
|
782
|
+
def automatic(args, core_factory: Callable | None = None) -> bool:
|
|
783
|
+
"""A real terminal session on a computer with a GPU: Pal gets its helper ready by itself and asks nothing at start."""
|
|
784
|
+
return (core_factory is None and sys.stdin.isatty() and sys.stdout.isatty() and not os.environ.get("PAL_SKIP_SETUP")
|
|
785
|
+
and not api_only(backend.hardware()))
|
|
773
786
|
|
|
774
787
|
|
|
775
788
|
def ensure_ready_line(args) -> str:
|
|
776
|
-
"""The local-model line again, after
|
|
789
|
+
"""The local-model line again, after a model was downloaded."""
|
|
777
790
|
from pal.terminal.setup import ensure_local
|
|
778
791
|
return ensure_local(ui.make_console(), backend.hardware(), args.host or backend.DEFAULT_HOST, interactive=False, choose=False).detail
|
|
779
792
|
|
|
780
793
|
|
|
781
|
-
def
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
794
|
+
def has_main_model(args, host: str) -> bool:
|
|
795
|
+
"""Is there a model to start with: one on the command line, or the last one used (local and installed, or an API model with its key)?"""
|
|
796
|
+
if args.provider or args.model:
|
|
797
|
+
return True
|
|
798
|
+
_, model = backend.pick_default(backend.local_info(host), backend.load_prefs())
|
|
799
|
+
return bool(model)
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
class Boot:
|
|
803
|
+
"""What the first screen needs before the real session exists: the agent and the model from the saved settings, nothing probed.
|
|
804
|
+
It lets pal cli draw its screen at once while the setup runs in the background."""
|
|
805
|
+
|
|
806
|
+
def __init__(self, args, prefs: dict):
|
|
807
|
+
self.mode = args.mode or prefs.get("mode", "build")
|
|
808
|
+
if self.mode not in MODES:
|
|
809
|
+
self.mode = "build"
|
|
810
|
+
self.provider = args.provider or prefs.get("provider", "") or backend.LOCAL
|
|
811
|
+
self.model = args.model or prefs.get("model", "") or "choose a model"
|
|
812
|
+
self.cwd = Path(getattr(args, "dir", ".") or ".").resolve()
|
|
813
|
+
self.brand = "bench" if getattr(args, "bench_screen", False) else "cli"
|
|
814
|
+
self.pending: list = []
|
|
815
|
+
self.role_models: dict = {}
|
|
816
|
+
self.turns, self.pal_tokens, self.saved, self.budget = 0, 0, 0, 0
|
|
817
|
+
self.notice, self.helper_model, self.local, self.project, self.local_status = "", "", True, None, ""
|
|
818
|
+
|
|
819
|
+
def model_for(self, mode: str | None = None) -> tuple[str, str]:
|
|
820
|
+
return self.provider, self.model
|
|
821
|
+
|
|
822
|
+
def toggle_mode(self) -> str:
|
|
823
|
+
self.mode = MODES[(MODES.index(self.mode) + 1) % len(MODES)]
|
|
824
|
+
return self.mode
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def quiet_pull(tag: str) -> bool:
|
|
828
|
+
"""`ollama pull` with no output: for the helper, downloaded in the background."""
|
|
829
|
+
import shutil
|
|
830
|
+
import subprocess
|
|
831
|
+
exe = shutil.which("ollama")
|
|
832
|
+
if not exe:
|
|
833
|
+
return False
|
|
834
|
+
try:
|
|
835
|
+
return subprocess.call([exe, "pull", tag], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
|
|
836
|
+
except OSError:
|
|
837
|
+
return False
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
def background_helper(hw, info: dict) -> None:
|
|
841
|
+
"""Pal's helper: the best small model for this computer. Installed: used. Missing: downloaded in a background thread (used from the next
|
|
842
|
+
start), so nothing waits for it."""
|
|
843
|
+
import threading
|
|
844
|
+
|
|
845
|
+
from pal.terminal.local_models import pick_helper
|
|
846
|
+
if api_only(hw) or not info.get("ok"):
|
|
847
|
+
return
|
|
848
|
+
best = pick_helper(hw).tag
|
|
849
|
+
if best in backend.chat_models(info):
|
|
850
|
+
backend.save_prefs(helper_model=best)
|
|
851
|
+
return
|
|
852
|
+
|
|
853
|
+
def get() -> None:
|
|
854
|
+
if quiet_pull(best):
|
|
855
|
+
backend.save_prefs(helper_model=best)
|
|
856
|
+
threading.Thread(target=get, daemon=True, name="pal-helper-download").start()
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def prepare_quietly(args, core_factory: Callable | None, out: dict) -> None:
|
|
860
|
+
"""Everything a session needs, without a word on the screen: the hardware, Ollama (started if needed), the folder (documents to markdown,
|
|
861
|
+
the map, the graph), the helper, and the session itself. What needs the person (a model to choose, a key) is left to the foreground."""
|
|
862
|
+
import io
|
|
863
|
+
|
|
864
|
+
from rich.console import Console
|
|
865
|
+
from pal.terminal.project import Project
|
|
866
|
+
from pal.terminal.setup import ensure_local
|
|
867
|
+
quiet = Console(file=io.StringIO())
|
|
868
|
+
try:
|
|
869
|
+
host = args.host or backend.DEFAULT_HOST
|
|
870
|
+
if core_factory is None:
|
|
871
|
+
hw = backend.hardware()
|
|
872
|
+
out["local"] = ensure_local(quiet, hw, host, interactive=False, choose=False).detail
|
|
873
|
+
info = backend.local_info(host)
|
|
874
|
+
if automatic(args, core_factory):
|
|
875
|
+
background_helper(hw, info)
|
|
876
|
+
project = None
|
|
877
|
+
if not os.environ.get("PAL_SKIP_PROJECT"):
|
|
878
|
+
try:
|
|
879
|
+
project = Project(Path(getattr(args, "dir", ".") or "."))
|
|
880
|
+
project.prepare()
|
|
881
|
+
except Exception: # noqa: BLE001 (the session still starts without the folder's map, graph and documents)
|
|
882
|
+
project = None
|
|
883
|
+
out["project"] = project
|
|
884
|
+
out["need_model"] = automatic(args, core_factory) and not has_main_model(args, host)
|
|
885
|
+
if not out["need_model"]:
|
|
886
|
+
out["session"] = start(args, core_factory, project, prompt=False)
|
|
887
|
+
except Exception as e: # noqa: BLE001 (the foreground tries again and shows what went wrong)
|
|
888
|
+
out["error"] = e
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
def finish_session(args, core_factory: Callable | None, ready: dict, console, boot: Boot) -> PalSession | None:
|
|
892
|
+
"""The real session, once the background setup is done. Asks the person only what it must (a first model, an API key)."""
|
|
893
|
+
session = ready.get("session")
|
|
796
894
|
if session is None:
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
895
|
+
host = args.host or backend.DEFAULT_HOST
|
|
896
|
+
if ready.get("need_model"):
|
|
897
|
+
from pal.terminal.setup import choose_models
|
|
898
|
+
if not choose_models(console, backend.hardware(), host, helper=False):
|
|
899
|
+
console.print(Text("\nNo model chosen. Run pal cli again to choose one.\n", style=theme.MUTED))
|
|
900
|
+
return None
|
|
901
|
+
session = start(args, core_factory, ready.get("project"))
|
|
902
|
+
if session is None:
|
|
903
|
+
return None
|
|
904
|
+
session.mode = boot.mode
|
|
905
|
+
session.local_status = ready.get("local", "")
|
|
906
|
+
session.brand = boot.brand
|
|
907
|
+
return session
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def interactive(args, core_factory: Callable | None = None) -> int:
|
|
911
|
+
"""pal cli: the screen is drawn at once; the setup (Ollama, the folder, the helper, the model) runs in the background while you type.
|
|
912
|
+
The model is changed by clicking it (or /model). Only when there is no model at all yet is the person asked for one."""
|
|
913
|
+
import threading
|
|
914
|
+
|
|
810
915
|
from pal.terminal.history import History
|
|
811
|
-
|
|
916
|
+
console = ui.make_console()
|
|
917
|
+
boot = Boot(args, backend.load_prefs())
|
|
918
|
+
ready: dict = {}
|
|
919
|
+
prep = threading.Thread(target=prepare_quietly, args=(args, core_factory, ready), daemon=True, name="pal-setup")
|
|
920
|
+
prep.start()
|
|
921
|
+
history = History(boot.cwd)
|
|
922
|
+
state: dict = {"session": None, "box": None}
|
|
923
|
+
|
|
924
|
+
def session_now() -> PalSession | None:
|
|
925
|
+
"""The real session: waits for the background setup the first time (only if it has not finished yet)."""
|
|
926
|
+
if state["session"] is None:
|
|
927
|
+
if prep.is_alive():
|
|
928
|
+
with console.status(Text("Getting ready\u2026", style=theme.MUTED), spinner="dots", spinner_style=theme.RED):
|
|
929
|
+
prep.join()
|
|
930
|
+
s = finish_session(args, core_factory, ready, console, boot)
|
|
931
|
+
if s is None:
|
|
932
|
+
return None
|
|
933
|
+
state["session"], state["box"] = s, {"wf": Workflow(s)}
|
|
934
|
+
_bind_status(state["box"]["wf"])
|
|
935
|
+
return state["session"]
|
|
936
|
+
|
|
937
|
+
first, draft, code = True, "", 0
|
|
812
938
|
try:
|
|
813
939
|
while True:
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
940
|
+
shown = state["session"] or boot
|
|
941
|
+
box = state["box"]
|
|
942
|
+
text = ui.read_input(shown, console, backend.save_prefs, landing=first, version=__version__, history=history, initial=draft,
|
|
943
|
+
on_shift_tab=(lambda: cycle_autonomy(state["box"]["wf"])) if box else None)
|
|
944
|
+
landing, first, shown.notice, draft = first, False, "", ""
|
|
817
945
|
if text is None:
|
|
818
946
|
break
|
|
819
|
-
if text.startswith("\x00voice\x00"): # Ctrl+Space: record, then back to the box with the words added
|
|
947
|
+
if text.startswith("\x00voice\x00"): # the mic (click or Ctrl+Space): record, then back to the box with the words added
|
|
820
948
|
before = text[len("\x00voice\x00"):]
|
|
821
949
|
heard = voice_flow(console)
|
|
822
950
|
draft = (before + (" " if before and heard else "") + heard) if heard else before
|
|
951
|
+
first = landing
|
|
952
|
+
continue
|
|
953
|
+
session = session_now()
|
|
954
|
+
if session is None:
|
|
955
|
+
code = 1
|
|
956
|
+
break
|
|
957
|
+
box = state["box"]
|
|
958
|
+
if text.startswith("\x00model\x00"): # the model was clicked on the first screen: choose one, then back to it
|
|
959
|
+
draft = text[len("\x00model\x00"):]
|
|
960
|
+
state["session"], _ = handle("/model", args, session, console, core_factory, box)
|
|
961
|
+
first = landing
|
|
823
962
|
continue
|
|
824
963
|
history.add(text)
|
|
825
964
|
text = text.strip()
|
|
@@ -833,7 +972,7 @@ def interactive(args, core_factory: Callable | None = None) -> int:
|
|
|
833
972
|
continue
|
|
834
973
|
try:
|
|
835
974
|
if text.startswith("/"):
|
|
836
|
-
session, go = handle(text, args, session, console, core_factory, box)
|
|
975
|
+
state["session"], go = handle(text, args, session, console, core_factory, box)
|
|
837
976
|
if not go:
|
|
838
977
|
break
|
|
839
978
|
elif text and getattr(args, "bench_screen", False): # pal bench: the task both ways, then the comparison
|
|
@@ -850,10 +989,12 @@ def interactive(args, core_factory: Callable | None = None) -> int:
|
|
|
850
989
|
ui.say(console, Text("Stopped.", style=theme.MUTED))
|
|
851
990
|
console.print()
|
|
852
991
|
finally:
|
|
853
|
-
|
|
854
|
-
|
|
992
|
+
if state["box"] is not None:
|
|
993
|
+
state["box"]["wf"].close()
|
|
994
|
+
if state["session"] is not None:
|
|
995
|
+
state["session"].close()
|
|
855
996
|
console.print(Text("Bye.", style=theme.MUTED))
|
|
856
|
-
return
|
|
997
|
+
return code
|
|
857
998
|
|
|
858
999
|
|
|
859
1000
|
def ask_once(args, core_factory: Callable | None = None) -> int:
|
|
@@ -398,7 +398,7 @@ def _get(console: Console, tag: str, installed: list[str], confirm: Callable[[st
|
|
|
398
398
|
|
|
399
399
|
def choose_models(console: Console, hw: Hardware, host: str = backend.DEFAULT_HOST, pick: Callable[..., str | None] | None = None,
|
|
400
400
|
puller: Callable[[str], bool] = pull, api_chooser: Callable | None = None, confirm: Callable[[str], bool] | None = None,
|
|
401
|
-
info: dict | None = None) -> bool:
|
|
401
|
+
info: dict | None = None, helper: bool = True) -> bool:
|
|
402
402
|
"""Ask which models to use for this session; nothing is chosen in advance. The main model is an external API model (any provider, the key
|
|
403
403
|
is never saved) or a local model that suits this GPU (installed ones are marked; others are downloaded after asking). Then the helper: a small
|
|
404
404
|
local model for Pal's own jobs, or none. The choice is saved. False when the person chose nothing."""
|
|
@@ -428,6 +428,9 @@ def choose_models(console: Console, hw: Hardware, host: str = backend.DEFAULT_HO
|
|
|
428
428
|
if not ready:
|
|
429
429
|
backend.save_prefs(helper_model="", setup_done="1")
|
|
430
430
|
return True
|
|
431
|
+
if not helper: # Pal chooses the helper itself (auto_helper)
|
|
432
|
+
backend.save_prefs(setup_done="1")
|
|
433
|
+
return True
|
|
431
434
|
main = "" if answer == API_OPTION else answer
|
|
432
435
|
helpers = helper_choices(hw, installed, exclude=main) + [(NO_HELPER, "the main model also does Pal's small jobs (uses more of its tokens)")]
|
|
433
436
|
helper = pick("Which small local helper should do Pal's own jobs? (routing easy questions, trimming what you type)", helpers, None, True, "Helper")
|
|
@@ -453,3 +456,25 @@ def choose_agent(console: Console, pick: Callable[..., str | None] | None = None
|
|
|
453
456
|
if mode:
|
|
454
457
|
backend.save_prefs(mode=mode)
|
|
455
458
|
return mode
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def auto_helper(console: Console, hw: Hardware, host: str = backend.DEFAULT_HOST, puller: Callable[[str], bool] = pull,
|
|
462
|
+
info: dict | None = None) -> str:
|
|
463
|
+
"""Pal chooses its own helper: the best small model for this computer. Already installed: used as it is. Not installed: downloaded now
|
|
464
|
+
(Ollama shows its progress). Nothing is asked. Returns the helper's name, or '' when there is none (no GPU, Ollama not running, failed)."""
|
|
465
|
+
from pal.terminal.local_models import api_only, pick_helper
|
|
466
|
+
if api_only(hw):
|
|
467
|
+
return ""
|
|
468
|
+
info = info if info is not None else backend.local_info(host)
|
|
469
|
+
if not info.get("ok"):
|
|
470
|
+
return ""
|
|
471
|
+
best = pick_helper(hw)
|
|
472
|
+
if best.tag not in backend.chat_models(info):
|
|
473
|
+
_say(console, Text(f"Getting Pal's helper model ready: {best.tag} (about {best.size_gb:g} GB, once). It does Pal's small jobs, like trimming what you type.",
|
|
474
|
+
style=theme.MUTED))
|
|
475
|
+
if not puller(best.tag):
|
|
476
|
+
_say(console, Text(f"The helper {best.tag} could not be downloaded: Pal works without it for now and tries again next time.", style=theme.MUTED))
|
|
477
|
+
backend.save_prefs(helper_model="")
|
|
478
|
+
return ""
|
|
479
|
+
backend.save_prefs(helper_model=best.tag)
|
|
480
|
+
return best.tag
|