pal-code 0.1.0__tar.gz → 0.1.2__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.0 → pal_code-0.1.2}/LICENSE +1 -1
- {pal_code-0.1.0 → pal_code-0.1.2}/PKG-INFO +2 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/pyproject.toml +2 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/__init__.py +1 -1
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/cli.py +5 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/__init__.py +7 -7
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/utils/__init__.py +5 -5
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/utils/relevance.py +1 -1
- pal_code-0.1.2/src/pal/context_window.py +42 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/local_llm/components/huggingface_local.py +1 -1
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/components/vector_store_backend.py +1 -1
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/routes.py +2 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/runs.py +21 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/bench.py +8 -1
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/cli.py +111 -20
- pal_code-0.1.2/src/pal/terminal/logo.py +140 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/session.py +13 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/setup.py +104 -3
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/ui.py +53 -29
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/components/tool_registry.py +1 -1
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/app.py +6 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/studio.css +2 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/templates/index.html +14 -6
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/templates/studio.html +2 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal_code.egg-info/PKG-INFO +2 -2
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal_code.egg-info/SOURCES.txt +1 -0
- pal_code-0.1.0/src/pal/terminal/logo.py +0 -117
- {pal_code-0.1.0 → pal_code-0.1.2}/README.md +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/setup.cfg +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/__main__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/components/access_control.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/components/intervention.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/components/privilege_registry.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/access_manager/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agent_constraints/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agent_constraints/components/AGENT_OUTPUT.md +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agent_constraints/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agent_constraints/components/constraints.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agent_constraints/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agent_constraints/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agents/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agents/coding.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/agents/docs.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/demo.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/providers.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/service.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/services/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/services/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/services/data.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/services/media.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/services/web.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/sessions.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/tools.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/chat/uploads.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/adverbs.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/hedges.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/interjections.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/minifier.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/output_compressor.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/pleonasms.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/punctuations.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/components/stopwords.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/history.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/utils/negations.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/utils/nltk_setup.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/utils/pronouns.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/utils/proselint_setup.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/compression/utils/spacy_setup.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/config/default.yaml +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/config.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/chat_context.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/assembler.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/context_cache.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/kv_bridge.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/pager.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/simple_context_manager.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/components/turns.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/utils/hashing.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/context_manager/utils/storage.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/documents/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/documents/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/documents/components/chunker.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/documents/components/index.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/documents/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/errors.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/components/pandoc_converter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/components/pdf_converter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/components/pptx_converter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/components/xlsx_converter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_converters/workspace.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/INPUT_RULES.md +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/components/methods.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/components/verifier.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/input_savers/utils/guards.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/components/builder.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/components/retriever.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/components/workspace.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/knowledge_graph/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/latency_tracker/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/latency_tracker/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/api.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/code.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/core.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/local.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_scheduler/types.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/adapter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/components/adapter_types.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/components/features.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/components/litellm_wrapper.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/components/openrouter_wrapper.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/modes.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/utils/registry.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/llm_wrapper/utils/token_counting.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/local_llm/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/local_llm/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/local_llm/components/local_llm.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/logging_setup.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/components/lru_k.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/components/manager.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/components/note.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/components/storage_manager.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory/utils/keyword_overlap.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory_tracker/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory_tracker/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory_tracker/components/process.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/memory_tracker/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/metrics/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/metrics/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/metrics/components/collector.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/metrics/components/exporter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/metrics/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/metrics/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/notes_manager/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/notes_manager/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/notes_manager/components/privacy.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/notes_manager/components/recall.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/notes_manager/components/tidy.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/notes_manager/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/agent.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/cgroup_manager.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/context_interrupt.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/limits.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/scheduler.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/components/syscall_scheduler.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/utils/device.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/orchestrator/utils/protocol.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/os_kernel/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/os_kernel/filesystem.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/os_kernel/network.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/os_kernel/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/os_kernel/sysinfo.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/paths.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/py.typed +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/redact.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/adapters.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/agent.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/autogen_adapter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/autogen_client.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/framework_adapter_base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/metagpt_adapter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/open_interpreter_adapter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/components/tool_catalog.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/os_api.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/patterns.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/queries.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/savers.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/sdk/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/components/local_file_backend.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/components/storage_queue.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/utils/memory_adapter.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/storage_manager/utils/sdk.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/builder.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/catalog.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/checks.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/clients.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/editor.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/files.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/models.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/plan.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/planner.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/studio/policy.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/components/answerer.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/components/classifier.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/components/gates.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/task_router/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/backend.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/clients.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/commands.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/decide.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/files.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/flow_cli.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/hardware.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/history.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/keys.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/local_models.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/models.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/policy.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/project.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/review.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/security.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/syntax.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/theme.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/voice.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/terminal/workflow.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/token_tracker/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/token_tracker/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tokens.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_catalog.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/builtin.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/components/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/components/base.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/components/conflict_resolver.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/components/param_validator.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/components/tool_call_queue.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/pipeline.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/tool_manager/utils/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/__init__.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/editor.js +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/fonts/LICENSE-Inter.txt +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/fonts/inter-400.woff2 +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/fonts/inter-500.woff2 +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/fonts/inter-600.woff2 +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/fonts/inter-700.woff2 +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/inter.css +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/pal-logo-256.png +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/pal-logo-32.png +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/pal-logo-64.png +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/studio.js +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal/web/static/voice.js +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal_code.egg-info/dependency_links.txt +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal_code.egg-info/entry_points.txt +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal_code.egg-info/requires.txt +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/src/pal_code.egg-info/top_level.txt +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_cli.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_config.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_lockfile.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_paths.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_redact.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_run_script.py +0 -0
- {pal_code-0.1.0 → pal_code-0.1.2}/tests/test_tool_catalog.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pal-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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
|
-
Author:
|
|
5
|
+
Author: Vrushal More
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: ai,coding-assistant,cli,llm,ollama,tokens,code-graph
|
|
8
8
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pal-code"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
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"
|
|
11
11
|
license = { text = "MIT" }
|
|
12
|
-
authors = [{ name = "
|
|
12
|
+
authors = [{ name = "Vrushal More" }]
|
|
13
13
|
keywords = ["ai", "coding-assistant", "cli", "llm", "ollama", "tokens", "code-graph"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 4 - Beta",
|
|
@@ -212,7 +212,7 @@ 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) -> int:
|
|
215
|
+
def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph=None, documents: list | None = None, project_dir=None) -> int:
|
|
216
216
|
from pal.chat import ChatService
|
|
217
217
|
from pal.web import create_app
|
|
218
218
|
|
|
@@ -221,7 +221,7 @@ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph
|
|
|
221
221
|
service = ChatService(settings, graph=graph) if graph is not None else ChatService(settings)
|
|
222
222
|
if documents:
|
|
223
223
|
service.preload_docs = list(documents)
|
|
224
|
-
app = create_app(service)
|
|
224
|
+
app = create_app(service, studio={"project_dir": project_dir}) if project_dir else create_app(service)
|
|
225
225
|
if s.host not in _LOOPBACK:
|
|
226
226
|
print(f"WARNING: listening on {s.host}. Pal has no login: anyone who can reach this port can use "
|
|
227
227
|
"your API keys' quota and read stored memory. Put it behind an authenticating reverse proxy.",
|
|
@@ -239,6 +239,9 @@ def _serve(settings: Settings, open_browser: bool = False, page: str = "", graph
|
|
|
239
239
|
"the app still works and sends everything to the API model.", file=sys.stderr)
|
|
240
240
|
else:
|
|
241
241
|
print(f"Local model: {status['detail']}")
|
|
242
|
+
import threading as _t
|
|
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
|
|
242
245
|
if open_browser:
|
|
243
246
|
import threading
|
|
244
247
|
import webbrowser
|
|
@@ -16,37 +16,37 @@ TextMinifier itself can't be built for the same reason.
|
|
|
16
16
|
|
|
17
17
|
try:
|
|
18
18
|
from pal.compression.components.adverbs import Adverbs
|
|
19
|
-
except
|
|
19
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
20
20
|
Adverbs = None
|
|
21
21
|
|
|
22
22
|
try:
|
|
23
23
|
from pal.compression.components.hedges import Hedges
|
|
24
|
-
except
|
|
24
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
25
25
|
Hedges = None
|
|
26
26
|
|
|
27
27
|
try:
|
|
28
28
|
from pal.compression.components.interjections import Interjections
|
|
29
|
-
except
|
|
29
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
30
30
|
Interjections = None
|
|
31
31
|
|
|
32
32
|
try:
|
|
33
33
|
from pal.compression.components.pleonasms import Pleonasms
|
|
34
|
-
except
|
|
34
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
35
35
|
Pleonasms = None
|
|
36
36
|
|
|
37
37
|
try:
|
|
38
38
|
from pal.compression.components.punctuations import Punctuations
|
|
39
|
-
except
|
|
39
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
40
40
|
Punctuations = None
|
|
41
41
|
|
|
42
42
|
try:
|
|
43
43
|
from pal.compression.components.stopwords import Stopwords
|
|
44
|
-
except
|
|
44
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
45
45
|
Stopwords = None
|
|
46
46
|
|
|
47
47
|
try:
|
|
48
48
|
from pal.compression.components.minifier import TextMinifier
|
|
49
|
-
except
|
|
49
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
50
50
|
TextMinifier = None
|
|
51
51
|
|
|
52
52
|
# No optional deps -- always available.
|
|
@@ -4,27 +4,27 @@ raise straight out of this package's __init__."""
|
|
|
4
4
|
|
|
5
5
|
try:
|
|
6
6
|
from pal.compression.utils.negations import Negations
|
|
7
|
-
except
|
|
7
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
8
8
|
Negations = None
|
|
9
9
|
|
|
10
10
|
try:
|
|
11
11
|
from pal.compression.utils.nltk_setup import NltkSetup
|
|
12
|
-
except
|
|
12
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
13
13
|
NltkSetup = None
|
|
14
14
|
|
|
15
15
|
try:
|
|
16
16
|
from pal.compression.utils.proselint_setup import ProselintSetup
|
|
17
|
-
except
|
|
17
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
18
18
|
ProselintSetup = None
|
|
19
19
|
|
|
20
20
|
try:
|
|
21
21
|
from pal.compression.utils.pronouns import Pronouns
|
|
22
|
-
except
|
|
22
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
23
23
|
Pronouns = None
|
|
24
24
|
|
|
25
25
|
try:
|
|
26
26
|
from pal.compression.utils.spacy_setup import SpacySetup
|
|
27
|
-
except
|
|
27
|
+
except Exception: # missing OR broken (e.g. a torch DLL error)
|
|
28
28
|
SpacySetup = None
|
|
29
29
|
|
|
30
30
|
__all__ = ["Negations", "NltkSetup", "ProselintSetup", "Pronouns", "SpacySetup"]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""How many tokens a model can take in one request (its context window), for the "tokens left" counters in the terminal and the web page.
|
|
2
|
+
|
|
3
|
+
API models: the size LiteLLM knows for the model, else a safe default. Local models: the context Pal gives Ollama (num_ctx), which is what
|
|
4
|
+
really limits them here, whatever the model could do on paper."""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
|
|
9
|
+
LOCAL_CONTEXT = 8192 # Pal runs local models with num_ctx 8192 (pal.terminal.backend.make_core)
|
|
10
|
+
API_DEFAULT = 128_000 # a model LiteLLM does not know: most current API models take at least this
|
|
11
|
+
LOCAL_PROVIDERS = ("local", "ollama", "lmstudio", "vllm", "llamacpp", "llama.cpp", "huggingface")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@lru_cache(maxsize=256)
|
|
15
|
+
def context_window(provider: str, model: str) -> int:
|
|
16
|
+
provider = (provider or "").lower()
|
|
17
|
+
if provider in LOCAL_PROVIDERS:
|
|
18
|
+
return LOCAL_CONTEXT
|
|
19
|
+
if not model:
|
|
20
|
+
return API_DEFAULT
|
|
21
|
+
try:
|
|
22
|
+
import litellm
|
|
23
|
+
for name in (f"{provider}/{model}" if provider else model, model, model.split("/")[-1]):
|
|
24
|
+
try:
|
|
25
|
+
info = litellm.get_model_info(name)
|
|
26
|
+
except Exception: # noqa: BLE001 (not in LiteLLM's table under this name)
|
|
27
|
+
continue
|
|
28
|
+
size = info.get("max_input_tokens") or info.get("max_tokens")
|
|
29
|
+
if size:
|
|
30
|
+
return int(size)
|
|
31
|
+
except Exception: # noqa: BLE001 (LiteLLM missing or broken: the default)
|
|
32
|
+
pass
|
|
33
|
+
return API_DEFAULT
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def tokens_left(window: int, last_prompt: int) -> int:
|
|
37
|
+
"""What is left of the window after the last request's input (the next one is usually at least as big)."""
|
|
38
|
+
return max(0, window - max(0, last_prompt))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def short(n: int) -> str:
|
|
42
|
+
return f"{n / 1000:.1f}k".replace(".0k", "k") if n >= 10_000 else f"{n:,}"
|
|
@@ -68,7 +68,7 @@ class HuggingFaceLocalLLM(LLMWrapper):
|
|
|
68
68
|
try:
|
|
69
69
|
import torch
|
|
70
70
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
71
|
-
except ImportError as e:
|
|
71
|
+
except (ImportError, OSError) as e:
|
|
72
72
|
raise ImportError(
|
|
73
73
|
"transformers and torch are required for the in-process local "
|
|
74
74
|
"core. Run: pip install torch transformers"
|
|
@@ -49,7 +49,7 @@ def clean_references(raw) -> list[dict]:
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def register_studio(app, service, *, planner_core: Callable | None = None, core_factory: Callable | None = None, discover_fn: Callable | None = None,
|
|
52
|
-
hot_turns: int | None = None) -> RunStore:
|
|
52
|
+
hot_turns: int | None = None, project_dir: Path | None = None) -> RunStore:
|
|
53
53
|
"""Add the Studio page and endpoints to the app. The three callables are for tests: the planner's model, the builder's model, local-model discovery."""
|
|
54
54
|
cfg = service.settings
|
|
55
55
|
home = Path(cfg.storage_dir).expanduser() if cfg.storage_dir else data_dir()
|
|
@@ -71,7 +71,7 @@ def register_studio(app, service, *, planner_core: Callable | None = None, core_
|
|
|
71
71
|
raise UserInputError("no local model is chosen: start Ollama and pull a model, or switch to an API model")
|
|
72
72
|
return local_core(m["model"], num_ctx())
|
|
73
73
|
|
|
74
|
-
runs = RunStore(home / "studio", core_factory or default_core_factory, max_runs=cfg.studio.max_runs, hot_turns=hot_turns)
|
|
74
|
+
runs = RunStore(home / "studio", core_factory or default_core_factory, max_runs=cfg.studio.max_runs, hot_turns=hot_turns, project_dir=project_dir)
|
|
75
75
|
app.extensions["studio_runs"] = runs
|
|
76
76
|
|
|
77
77
|
def local() -> dict:
|
|
@@ -95,9 +95,22 @@ class Run:
|
|
|
95
95
|
return self.arms[name]
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
def build_target(project: Path, folder: str) -> Path:
|
|
99
|
+
"""Where a build is written in the folder Pal was started in: the folder itself when it is empty, else a new subfolder named after the
|
|
100
|
+
product (with -2, -3 ... when that name is taken), so nothing that is already there is overwritten."""
|
|
101
|
+
project = Path(project)
|
|
102
|
+
if not any(p for p in project.iterdir() if not p.name.startswith(".")):
|
|
103
|
+
return project
|
|
104
|
+
target, n = project / folder, 2
|
|
105
|
+
while target.exists() and (not target.is_dir() or any(target.iterdir())):
|
|
106
|
+
target, n = project / f"{folder}-{n}", n + 1
|
|
107
|
+
return target
|
|
108
|
+
|
|
109
|
+
|
|
98
110
|
class RunStore:
|
|
99
|
-
def __init__(self, root: Path, core_factory: Callable, max_runs: int = 6, hot_turns: int | None = None):
|
|
111
|
+
def __init__(self, root: Path, core_factory: Callable, max_runs: int = 6, hot_turns: int | None = None, project_dir: Path | None = None):
|
|
100
112
|
self.root, self.core_factory, self.max_runs, self.hot_turns = Path(root), core_factory, max_runs, hot_turns
|
|
113
|
+
self.project_dir = Path(project_dir).resolve() if project_dir else None # pal web's folder: the build is written there
|
|
101
114
|
self._runs: dict[str, Run] = {}
|
|
102
115
|
self._editors: dict[tuple[str, str], EditorSession] = {}
|
|
103
116
|
self._lock = threading.Lock()
|
|
@@ -140,11 +153,17 @@ class RunStore:
|
|
|
140
153
|
break
|
|
141
154
|
policy = Policy(dict(plan["permissions"]))
|
|
142
155
|
save = plan["permissions"].get("write_project")
|
|
143
|
-
|
|
156
|
+
where = None
|
|
157
|
+
if save:
|
|
158
|
+
where = (build_target(self.project_dir, plan["product"]["folder"]) if self.project_dir and arm != "baseline"
|
|
159
|
+
else self.root / run.id / arm / plan["product"]["folder"]) # the plain client's comparison copy stays in Pal's folder
|
|
160
|
+
state = ArmState(arm, label, where)
|
|
144
161
|
run.arms[arm] = state
|
|
145
162
|
if state.workspace:
|
|
146
163
|
state.workspace.mkdir(parents=True, exist_ok=True)
|
|
147
164
|
run.emit("arm_start", f"{label}: starting" if twice else "Starting", arm=arm)
|
|
165
|
+
if state.workspace and self.project_dir and arm != "baseline":
|
|
166
|
+
run.emit("saved_to", f"Files are written to {state.workspace}", arm=arm, path=str(state.workspace))
|
|
148
167
|
client: PlainClient | PalClient
|
|
149
168
|
if arm == "baseline":
|
|
150
169
|
client = PlainClient(core, SYSTEM)
|
|
@@ -67,6 +67,7 @@ class BenchResult:
|
|
|
67
67
|
plain: Arm
|
|
68
68
|
pal: Arm
|
|
69
69
|
when: float = field(default_factory=time.time)
|
|
70
|
+
pal_files: dict = field(default_factory=dict, repr=False) # what Pal's way wrote: {path: bytes}, offered to keep in your folder
|
|
70
71
|
|
|
71
72
|
@property
|
|
72
73
|
def saved(self) -> int:
|
|
@@ -78,6 +79,7 @@ class BenchResult:
|
|
|
78
79
|
|
|
79
80
|
def to_dict(self) -> dict:
|
|
80
81
|
d = asdict(self)
|
|
82
|
+
d.pop("pal_files", None)
|
|
81
83
|
d.update(saved=self.saved, saved_pct=self.saved_pct, plain_total=self.plain.total, pal_total=self.pal.total)
|
|
82
84
|
return d
|
|
83
85
|
|
|
@@ -161,7 +163,12 @@ def run_bench(folder: Path, task: str, core_factory: Callable[[], Any], model: s
|
|
|
161
163
|
pal_arm = _run_arm("pal", pal, steps, progress)
|
|
162
164
|
finally:
|
|
163
165
|
client.close()
|
|
164
|
-
|
|
166
|
+
kept = {}
|
|
167
|
+
for rel in sorted({f for st in pal_arm.steps for f in st.files}):
|
|
168
|
+
f = pal_dir / rel
|
|
169
|
+
if f.is_file():
|
|
170
|
+
kept[rel] = f.read_bytes()
|
|
171
|
+
result = BenchResult(task, model, str(folder), plain_arm, pal_arm, pal_files=kept)
|
|
165
172
|
save(result, home)
|
|
166
173
|
return result
|
|
167
174
|
|
|
@@ -70,7 +70,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
70
70
|
a.add_argument("--build", action="store_true", help="use the Build agent: it may propose file changes (asks before writing)")
|
|
71
71
|
b = sub.add_parser("bench", help="run one task as a plain chat and through Pal, on the same model, and compare the real tokens")
|
|
72
72
|
_common(b, suppress=True)
|
|
73
|
-
b.add_argument("task", nargs="
|
|
73
|
+
b.add_argument("task", nargs="*", help="what to build, as you would type it (leave it out to open the bench screen and type tasks there)")
|
|
74
74
|
b.add_argument("--then", action="append", default=None, metavar="MESSAGE", help="a follow-up message (repeat for more; default: two standard ones)")
|
|
75
75
|
b.add_argument("--no-follow-ups", action="store_true", help="only the task itself")
|
|
76
76
|
b.add_argument("--json", action="store_true", help="print the result as JSON")
|
|
@@ -79,13 +79,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
79
79
|
return p
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def bench(args) -> int:
|
|
83
|
-
"""pal bench: the same task, plain chat versus Pal, on the same model.""
|
|
82
|
+
def bench(args, core_factory: Callable | None = None) -> int:
|
|
83
|
+
"""pal bench: the same task, plain chat versus Pal, on the same model. Without a task it opens the usual Pal screen (\"pal bench\"), and every
|
|
84
|
+
task typed there is run both ways, with the results as a table and bar charts."""
|
|
84
85
|
import json as _json
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
if not args.task:
|
|
88
|
+
if not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
89
|
+
print("pal bench: give a task, e.g. pal bench \"make a todo app\"", file=sys.stderr)
|
|
90
|
+
return 2
|
|
91
|
+
args.bench_screen = True
|
|
92
|
+
return interactive(args, core_factory)
|
|
89
93
|
console = ui.make_console()
|
|
90
94
|
host = args.host or backend.DEFAULT_HOST
|
|
91
95
|
prefs = backend.load_prefs()
|
|
@@ -103,17 +107,36 @@ def bench(args) -> int:
|
|
|
103
107
|
except PalError as e:
|
|
104
108
|
print(f"pal bench: {e}", file=sys.stderr)
|
|
105
109
|
return 2
|
|
106
|
-
|
|
107
|
-
task = " ".join(args.task)
|
|
108
|
-
status = console.status(Text("Starting\u2026", style=theme.MUTED), spinner="dots", spinner_style=theme.RED)
|
|
109
|
-
with status:
|
|
110
|
-
result = run_bench(Path(args.dir), task, lambda: backend.make_core(provider, model, host), model, follow,
|
|
111
|
-
progress=lambda m: status.update(Text(m, style=theme.MUTED)))
|
|
110
|
+
result = bench_run(console, Path(args.dir), " ".join(args.task), provider, model, host, [] if args.no_follow_ups else args.then)
|
|
112
111
|
if args.json:
|
|
113
112
|
print(_json.dumps(result.to_dict(), indent=1))
|
|
114
113
|
return 0
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
show_bench(console, result, provider, model, Path(args.dir))
|
|
115
|
+
return 0
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def bench_run(console, folder: Path, task: str, provider: str, model: str, host: str, follow=None, core_factory: Callable | None = None):
|
|
119
|
+
from pal.terminal.bench import run_bench
|
|
120
|
+
status = console.status(Text("Starting\u2026", style=theme.MUTED), spinner="dots", spinner_style=theme.RED)
|
|
121
|
+
with status:
|
|
122
|
+
return run_bench(folder, task, lambda: (core_factory or backend.make_core)(provider, model, host), model, follow,
|
|
123
|
+
progress=lambda m: status.update(Text(m, style=theme.MUTED)))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def bars(console, title: str, rows: list[tuple[str, float, str]], width: int = 46) -> None:
|
|
127
|
+
"""A horizontal bar chart in the terminal: (label, value, shown value). Plain chat in grey, Pal in red."""
|
|
128
|
+
top = max((v for _, v, _ in rows), default=0) or 1
|
|
129
|
+
ui.say(console, Text(title, style=f"bold {theme.WHITE}"))
|
|
130
|
+
for label, value, shown in rows:
|
|
131
|
+
n = max(1, round(width * value / top)) if value > 0 else 0
|
|
132
|
+
colour = theme.RED if label.lower().startswith("pal") else theme.MUTED
|
|
133
|
+
ui.say(console, Text.assemble((" " + f"{label:<11}", theme.INK), ("\u2588" * n, colour), (" " * (width - n + 2), ""), (shown, f"bold {theme.WHITE}")))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def show_bench(console, result, provider: str, model: str, folder: Path) -> None:
|
|
137
|
+
from rich.table import Table
|
|
138
|
+
ui.say(console, Text.assemble(("pal bench ", f"bold {theme.WHITE}"), (f"{model} ({provider}) \u00B7 {ui.short_path(Path(folder).resolve())}", theme.MUTED)))
|
|
139
|
+
ui.say(console, Text(f"Task: {result.task}", style=theme.INK))
|
|
117
140
|
console.print()
|
|
118
141
|
grid = Table(box=None, padding=(0, 2), header_style=f"bold {theme.WHITE}")
|
|
119
142
|
for col, just in (("step", "left"), ("agent", "left"), ("plain chat", "right"), ("Pal", "right"), ("saved", "right")):
|
|
@@ -129,6 +152,13 @@ def bench(args) -> int:
|
|
|
129
152
|
Text(f"{result.saved:,} ({result.saved_pct:g}%)", style=f"bold {theme.RED_HI}"))
|
|
130
153
|
ui.say(console, grid)
|
|
131
154
|
console.print()
|
|
155
|
+
bars(console, "Tokens", [("plain chat", result.plain.total, f"{result.plain.total:,}"), ("Pal", result.pal.total, f"{result.pal.total:,}")])
|
|
156
|
+
console.print()
|
|
157
|
+
bars(console, "Time", [("plain chat", result.plain.seconds, f"{result.plain.seconds:g}s"), ("Pal", result.pal.seconds, f"{result.pal.seconds:g}s")])
|
|
158
|
+
console.print()
|
|
159
|
+
verdict = (f"Pal used {result.saved:,} fewer tokens ({result.saved_pct:g}% less)." if result.saved > 0 else
|
|
160
|
+
f"Pal used {-result.saved:,} more tokens on this task (it pays off with bigger projects, documents and longer conversations).")
|
|
161
|
+
ui.say(console, Text(verdict, style=f"bold {theme.RED_HI}"))
|
|
132
162
|
for arm in (result.plain, result.pal):
|
|
133
163
|
files = sorted({f for s in arm.steps for f in s.files})
|
|
134
164
|
ui.say(console, Text(f"{arm.name:<6} {arm.seconds:g}s \u00B7 {arm.calls} model call{'s' if arm.calls != 1 else ''} \u00B7 wrote: {', '.join(files) or 'no files'}"
|
|
@@ -137,7 +167,28 @@ def bench(args) -> int:
|
|
|
137
167
|
ui.say(console, Text("Token counts are the model's own." if not est else f"{est} call(s) had no token count from the model, so those are estimates (characters / 4).", style=theme.MUTED))
|
|
138
168
|
ui.say(console, Text("Pal's local helper jobs are off in the bench, so its saving here comes from the project map, graph, documents and history alone.", style=theme.MUTED))
|
|
139
169
|
console.print()
|
|
140
|
-
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def keep_bench_files(console, result, folder: Path) -> list[str]:
|
|
173
|
+
"""Both ways build in temporary copies; offer to write Pal's version into the folder (asking first, never overwriting without saying so)."""
|
|
174
|
+
if not result.pal_files:
|
|
175
|
+
return []
|
|
176
|
+
folder = Path(folder).resolve()
|
|
177
|
+
clash = [p for p in result.pal_files if (folder / p).exists()]
|
|
178
|
+
question = f"Save Pal's version ({len(result.pal_files)} file{'s' if len(result.pal_files) != 1 else ''}) in {ui.short_path(folder)}?" + (
|
|
179
|
+
f" It replaces {len(clash)} existing file{'s' if len(clash) != 1 else ''}." if clash else "")
|
|
180
|
+
if not ui.confirm(console, question):
|
|
181
|
+
return []
|
|
182
|
+
written = []
|
|
183
|
+
for rel, data in result.pal_files.items():
|
|
184
|
+
target = (folder / rel).resolve()
|
|
185
|
+
if folder not in target.parents:
|
|
186
|
+
continue # never outside the folder
|
|
187
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
188
|
+
target.write_bytes(data)
|
|
189
|
+
written.append(rel)
|
|
190
|
+
ui.say(console, Text(f"Saved: {', '.join(written)}", style=theme.INK))
|
|
191
|
+
return written
|
|
141
192
|
|
|
142
193
|
|
|
143
194
|
def setup(args) -> int:
|
|
@@ -165,7 +216,7 @@ def first_run() -> None:
|
|
|
165
216
|
console.print()
|
|
166
217
|
|
|
167
218
|
|
|
168
|
-
def prepare_folder(args, console, core_factory: Callable | None = None) -> tuple[str, Project | None]:
|
|
219
|
+
def prepare_folder(args, console, core_factory: Callable | None = None, choose: bool = True) -> tuple[str, Project | None]:
|
|
169
220
|
"""Before a session: get local models ready (install and start Ollama, download the models for this GPU, asking first), then set up the
|
|
170
221
|
folder: documents to markdown, the project map, the code graph (in the background). Returns (a line about the local model, the Project)."""
|
|
171
222
|
from pal.terminal.project import Project
|
|
@@ -173,7 +224,7 @@ def prepare_folder(args, console, core_factory: Callable | None = None) -> tuple
|
|
|
173
224
|
local = ""
|
|
174
225
|
if core_factory is None:
|
|
175
226
|
tty = sys.stdin.isatty() and sys.stdout.isatty()
|
|
176
|
-
local = ensure_local(console, backend.hardware(), args.host or backend.DEFAULT_HOST, interactive=tty).detail
|
|
227
|
+
local = ensure_local(console, backend.hardware(), args.host or backend.DEFAULT_HOST, interactive=tty, choose=choose).detail
|
|
177
228
|
folder = Path(getattr(args, "dir", ".") or ".")
|
|
178
229
|
if os.environ.get("PAL_SKIP_PROJECT"):
|
|
179
230
|
return local, None
|
|
@@ -217,7 +268,7 @@ def web(args) -> int:
|
|
|
217
268
|
from pal.terminal.project import ProjectGraph
|
|
218
269
|
graph = ProjectGraph(project.root / "graph", settings.knowledge_graph)
|
|
219
270
|
return _serve(settings, open_browser=not args.no_open, page="studio" if args.page == "studio" else "", graph=graph,
|
|
220
|
-
documents=project.documents() if project is not None else None)
|
|
271
|
+
documents=project.documents() if project is not None else None, project_dir=Path(getattr(args, "dir", ".") or ".").resolve())
|
|
221
272
|
|
|
222
273
|
|
|
223
274
|
def _utf8() -> None:
|
|
@@ -488,6 +539,14 @@ def handle(line: str, args, session: PalSession, console, core_factory, box: dic
|
|
|
488
539
|
return session, True
|
|
489
540
|
if cmd == "/help":
|
|
490
541
|
ui.show_help(console)
|
|
542
|
+
elif cmd == "/agent":
|
|
543
|
+
from pal.terminal.setup import choose_agent
|
|
544
|
+
mode = rest if rest in MODES else choose_agent(console, current=session.mode)
|
|
545
|
+
if mode:
|
|
546
|
+
session.mode = mode
|
|
547
|
+
backend.save_prefs(mode=mode)
|
|
548
|
+
ui.say(console, Text(f"Agent: {mode.capitalize()} ({AGENT_ABOUT[mode]})", style=theme.MUTED))
|
|
549
|
+
console.print()
|
|
491
550
|
elif cmd == "/mode":
|
|
492
551
|
if rest in MODES:
|
|
493
552
|
session.mode = rest
|
|
@@ -707,13 +766,37 @@ def voice_flow(console) -> str:
|
|
|
707
766
|
return heard.text
|
|
708
767
|
|
|
709
768
|
|
|
769
|
+
def choosing(args, core_factory: Callable | None = None) -> bool:
|
|
770
|
+
"""pal cli asks for its models at every start (nothing preselected), unless they were given on the command line or this is not a terminal."""
|
|
771
|
+
return (core_factory is None and not args.provider and not args.model and sys.stdin.isatty() and sys.stdout.isatty()
|
|
772
|
+
and not os.environ.get("PAL_SKIP_SETUP") and not api_only(backend.hardware()))
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def ensure_ready_line(args) -> str:
|
|
776
|
+
"""The local-model line again, after the models were chosen (and maybe downloaded)."""
|
|
777
|
+
from pal.terminal.setup import ensure_local
|
|
778
|
+
return ensure_local(ui.make_console(), backend.hardware(), args.host or backend.DEFAULT_HOST, interactive=False, choose=False).detail
|
|
779
|
+
|
|
780
|
+
|
|
710
781
|
def interactive(args, core_factory: Callable | None = None) -> int:
|
|
711
782
|
console = ui.make_console()
|
|
712
|
-
|
|
783
|
+
ask = choosing(args, core_factory)
|
|
784
|
+
local, project = prepare_folder(args, console, core_factory, choose=not ask)
|
|
785
|
+
if ask:
|
|
786
|
+
from pal.terminal.setup import choose_models
|
|
787
|
+
console.print()
|
|
788
|
+
if not choose_models(console, backend.hardware(), args.host or backend.DEFAULT_HOST):
|
|
789
|
+
console.print(Text("\nNo model chosen. Run pal cli again to choose one.\n", style=theme.MUTED))
|
|
790
|
+
return 1
|
|
791
|
+
if not args.mode:
|
|
792
|
+
from pal.terminal.setup import choose_agent
|
|
793
|
+
args.mode = choose_agent(console) or None
|
|
794
|
+
local = ensure_ready_line(args)
|
|
713
795
|
session = start(args, core_factory, project)
|
|
714
796
|
if session is None:
|
|
715
797
|
return 1
|
|
716
798
|
session.local_status = local
|
|
799
|
+
session.brand = "bench" if getattr(args, "bench_screen", False) else "cli"
|
|
717
800
|
if project is not None:
|
|
718
801
|
ui.show_project(console, session)
|
|
719
802
|
console.print()
|
|
@@ -753,6 +836,14 @@ def interactive(args, core_factory: Callable | None = None) -> int:
|
|
|
753
836
|
session, go = handle(text, args, session, console, core_factory, box)
|
|
754
837
|
if not go:
|
|
755
838
|
break
|
|
839
|
+
elif text and getattr(args, "bench_screen", False): # pal bench: the task both ways, then the comparison
|
|
840
|
+
host = args.host or backend.DEFAULT_HOST
|
|
841
|
+
try:
|
|
842
|
+
result = bench_run(console, session.cwd, text, session.provider, session.model, host, core_factory=core_factory)
|
|
843
|
+
show_bench(console, result, session.provider, session.model, session.cwd)
|
|
844
|
+
keep_bench_files(console, result, session.cwd)
|
|
845
|
+
except PalError as e:
|
|
846
|
+
ui.say(console, Text(f"The bench stopped: {e}", style=theme.RED_HI))
|
|
756
847
|
elif text:
|
|
757
848
|
flow_cli.run_turn(box["wf"], text, console)
|
|
758
849
|
except KeyboardInterrupt: # Esc or Ctrl+C at an awkward moment: back to the box, never out of Pal
|
|
@@ -813,7 +904,7 @@ def main(argv: Sequence[str] | None = None, core_factory: Callable | None = None
|
|
|
813
904
|
if args.cmd == "setup":
|
|
814
905
|
return setup(args)
|
|
815
906
|
if args.cmd == "bench":
|
|
816
|
-
return bench(args)
|
|
907
|
+
return bench(args, core_factory)
|
|
817
908
|
if args.cmd == "ask":
|
|
818
909
|
return ask_once(args, core_factory)
|
|
819
910
|
if not (sys.stdin.isatty() and sys.stdout.isatty()) and core_factory is None:
|