programasweights 0.1.0.dev5__tar.gz → 0.1.0.dev6__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.
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/PKG-INFO +1 -1
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/__init__.py +1 -1
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/pyproject.toml +1 -1
- programasweights-0.1.0.dev6/server/alembic/versions/002_namespaced_aliases_hf_url.py +37 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/models/orm.py +2 -1
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/compile.py +25 -2
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/programs.py +49 -28
- programasweights-0.1.0.dev6/server/api/services/storage_service.py +114 -0
- programasweights-0.1.0.dev6/server/scripts/cleanup_storage.py +66 -0
- programasweights-0.1.0.dev6/server/tests/test_storage.py +134 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/LandingPage.tsx +15 -15
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/MainInterface.tsx +17 -9
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/.gitignore +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/.hatch_build.toml +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/.readthedocs.yaml +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/1apple.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/1apple2.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/2apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/2apples2.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/3apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/3apples2.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/479400.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/4apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/4apples2.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/4apples3.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/4apples4.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/5apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/6apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/8apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/9apples.jpg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/FLOW_SUMMARY.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/MANIFEST.in +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/ONNX_MIGRATION_PLAN.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/PREFIX_TOKENS_DESIGN.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/PYPI_README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/TRUNCATION_CHANGES.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/USE_CASES_AND_IDEAS.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/VERIFICATION_USAGE.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/analyze_dataset.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/analyze_lengths.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/code_prompt.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/evaluate_openai_python_code_baseline.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/openai_batch_request.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/prepare_alchemist_data.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/prepare_var_bench_data.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/python_code_sandbox.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/script_evaluation_var_bench.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/script_evalution_evaluation.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/script_evalution_request.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/baselines/script_evalution_statistics.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/benchmark_pytorch_vs_onnx.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/benchmark_user_experience.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/check_dataset.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/combine_datasets.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/compare_datasets.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/compare_old_vs_regen.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/convert_paw_to_svg.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/create_favicon_sizes.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/create_visualization_from_log.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/create_vqa_dataset.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/debug_cache.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/debug_eos_example.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/Makefile +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/adr/001-llama-cpp-over-pytorch.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/adr/002-q4_0-adapter-format.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/adr/003-single-spec-field.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/adr/004-compiler-naming.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/adr/005-vllm-hidden-states.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/adr/006-email-api-key-auth.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/api-reference.rst +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/architecture.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/conf.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/examples/evaluation-tasks.rst +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/index.rst +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/installation.rst +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/make.bat +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/quickstart.rst +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/requirements.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/docs/using-pretrained.rst +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/estimate_data_gen_cost.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/eval.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/examples/flask_app.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/examples/jupyter_notebook.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/examples/langchain_integration.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/examples/replace_openai.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/export_to_onnx.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/extract_models.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/inspect_data_dirs.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/latest_export.csv +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/log.train.81920.morecategories.extraprefix +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/main_no_spec_direct_ans_mix_continuous_sampleref_shorterprompt_vllm.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/merge_datasets.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/paw.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/paw.svg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/process_im2latex_dataset.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/artifacts.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/cache.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/cli.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/client.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/compiler/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/compiler/dummy.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/config.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/convert_peft_to_paw.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/paw_format.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/runtime/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/runtime/interpreter.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/runtime/interpreter_onnx.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights/runtime_llamacpp.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/compiler/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/lora_format.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/runtime/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/runtime/interpreter_lora.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/tests/test_compile_and_run.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/tests/test_lora_format.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/tests/test_training.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/train_lora.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/training/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/programasweights_lora/training/loops/lora_tuning_sft.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/run_eval.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/run_training.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/scripts/filter_table_by_length.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/.env.example +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/alembic/env.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/alembic/script.py.mako +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/alembic/versions/001_initial_schema.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/alembic.ini +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/config.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/dependencies.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/logging_config.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/main.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/models/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/models/database.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/models/schemas.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/auth.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/health.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/infer.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/routes/models_info.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/services/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/services/compile_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/api/services/infer_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/benchmarks/benchmark_api.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/benchmarks/handcrafted_specs.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/benchmarks/last_benchmark_results.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/benchmarks/last_stress_results.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/benchmarks/stress_test.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/requirements.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/scripts/start_services.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/conftest.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/test_auth.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/test_compile.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/test_errors.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/test_infer.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/test_integration_gpu.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/tests/test_rate_limit.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/vllm_models/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/vllm_models/paw_compiler.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/vllm_models/prepare_checkpoint.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/server/vllm_models/register.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/stage2_firstpromptqwen_512_e1_generate_lora_e2grpo_overfit_one_debug.reward_plot.rollout_ppl.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/stage2_firstpromptqwen_512_e1_generate_lora_e2grpo_overfit_one_debug.reward_plot.train_gt_logprob.em_es.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/stage2_firstpromptqwen_512_e1_generate_lora_e2grpo_overfit_one_debug.reward_plot.train_gt_logprob.ppl.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/stage2_firstpromptqwen_512_e1_generate_lora_e2grpo_overfit_one_debug.reward_plot.train_gt_logprob.reward.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_1spec.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_all_caching.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_apple_count.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_batch_pilot.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_compile.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_e2e.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_execute.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_nspecs.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_one_vs_two_step.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_onnx_correctness.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_onnx_hf_model.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_onnx_hf_with_images.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_paw_format.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/test_thinking_comparison.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/tests/test_programasweights.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/train.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/train_no_spec_direct_ans_mix_continuous_sampleref_trainonly_shorterprompt_withregularizer_generate_lora.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/DATASET_CHANGELOG.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/VERIFICATION_PIPELINE.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/dry_run_batch.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/filter_test_data.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/generate_specs.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/generate_specs_batch.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/incremental_merge.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/merge_and_upload.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/regenerate_outputs_batch.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/synthesize_data.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/synthesize_data_batch.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/filter_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/filter_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/pairs_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/pairs_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/specs_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/specs_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/specs_user_freeform.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/specs_user_freeform_with_examples.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/specs_user_with_examples.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/verify_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates/verify_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/filter_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/filter_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/pairs_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/pairs_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/specs_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/specs_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/specs_user_freeform.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/specs_user_freeform_with_examples.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/specs_user_with_examples.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/verify_system.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/templates_old/verify_user.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/data_generation/verify_test_data.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/datasets/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/datasets/jsonl_text_pairs.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/loops/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/training/loops/prefix_tuning_sft.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/ttt.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/upload_model.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/upload_onnx_to_huggingface.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/upload_onnx_to_huggingface_with_token.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/utils.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/HANDOFF.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/config.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/main.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/middleware/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/middleware/rate_limit.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/models.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/auth_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/case_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/compiler_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/gpt_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/hub_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/image_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/interpreter_service.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/services/sql_manager.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/templates/gpt_test_generation.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/utils/__init__.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/utils/image_placeholders.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/app/utils/program_hash.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/clear_db.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/clear_tables.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/data/programs.db +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/env_example.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/example_images/README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/get_data_examples.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/requirements.txt +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/run_server.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_analytics_tables.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_auth_tables.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_cache_tables.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_cases_tables.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_database.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_database_simple.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_db.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_example_images.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/setup_hub_tables.sql +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/backend/test_compilation_cache.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/README.md +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/eslint.config.js +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/index.html +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/package-lock.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/package.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/postcss.config.js +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/apple-touch-icon.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/favicon-16x16.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/favicon-32x32.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/paw-192.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/paw-512.png +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/paw.svg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/react-test.html +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/test.html +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/public/vite.svg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/App.css +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/App.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/assets/react.svg +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/AboutPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/AutoTestSection.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/CasesSection.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/CollapsibleExamples.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/CompileButtonWithConfig.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/CompileSection.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/CopyCodeSection.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/DocsPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/ExampleRow/ExampleRowEditor.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/ExamplesInput.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/Footer.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/Header.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/HubPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/HubProgramPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/HubUploadPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/ImageUpload.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/InputBlocks/ImageBlock.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/InputBlocks/InputBlockList.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/InputBlocks/TextBlock.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/LeaderboardPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/ModelSelector.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/MultiImageUpload.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/PricingPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/PrivacyPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/ProfilePage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/ProgramDetailPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/PublishProgramPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/SpecInput.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/TermsPage.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/components/TestSection.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/index.css +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/main.tsx +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/types/index.ts +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/src/utils/api.ts +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/tailwind.config.js +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/tsconfig.app.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/tsconfig.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/tsconfig.node.json +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/frontend/vite.config.ts +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/nginx-paw.conf +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/nginx.conf +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/setup_mysql.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/start.sh +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/test_setup.py +0 -0
- {programasweights-0.1.0.dev5 → programasweights-0.1.0.dev6}/web-app/ttt.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: programasweights
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev6
|
|
4
4
|
Summary: Compile natural language specifications into neural programs that run locally via llama.cpp.
|
|
5
5
|
Author-email: ProgramAsWeights <support@programasweights.com>
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "programasweights"
|
|
7
|
-
version = "0.1.0.
|
|
7
|
+
version = "0.1.0.dev6"
|
|
8
8
|
description = "Compile natural language specifications into neural programs that run locally via llama.cpp."
|
|
9
9
|
readme = "PYPI_README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Add hf_url to programs, widen alias slug for username/name format
|
|
2
|
+
|
|
3
|
+
Revision ID: 002
|
|
4
|
+
Revises: 001
|
|
5
|
+
Create Date: 2026-03-26
|
|
6
|
+
"""
|
|
7
|
+
from typing import Sequence, Union
|
|
8
|
+
|
|
9
|
+
from alembic import op
|
|
10
|
+
import sqlalchemy as sa
|
|
11
|
+
|
|
12
|
+
revision: str = "002"
|
|
13
|
+
down_revision: Union[str, None] = "001"
|
|
14
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
15
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade() -> None:
|
|
19
|
+
op.add_column("programs", sa.Column("hf_url", sa.Text, nullable=True))
|
|
20
|
+
op.alter_column("aliases", "slug", type_=sa.String(120))
|
|
21
|
+
op.drop_constraint("ck_alias_slug_format", "aliases", type_="check")
|
|
22
|
+
op.create_check_constraint(
|
|
23
|
+
"ck_alias_slug_format",
|
|
24
|
+
"aliases",
|
|
25
|
+
"slug ~ '^[a-z0-9][a-z0-9-]*/[a-z0-9][a-z0-9-]*[a-z0-9]$' OR slug ~ '^[a-z0-9][a-z0-9-]*[a-z0-9]$'",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def downgrade() -> None:
|
|
30
|
+
op.drop_constraint("ck_alias_slug_format", "aliases", type_="check")
|
|
31
|
+
op.create_check_constraint(
|
|
32
|
+
"ck_alias_slug_format",
|
|
33
|
+
"aliases",
|
|
34
|
+
"slug ~ '^[a-z0-9][a-z0-9-]*[a-z0-9]$'",
|
|
35
|
+
)
|
|
36
|
+
op.alter_column("aliases", "slug", type_=sa.String(50))
|
|
37
|
+
op.drop_column("programs", "hf_url")
|
|
@@ -51,6 +51,7 @@ class Program(Base):
|
|
|
51
51
|
tags: Mapped[list[str] | None] = mapped_column(ARRAY(Text), nullable=True)
|
|
52
52
|
adapter_path: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
53
53
|
paw_path: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
54
|
+
hf_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
54
55
|
downloads: Mapped[int] = mapped_column(Integer, default=0)
|
|
55
56
|
upvotes: Mapped[int] = mapped_column(Integer, default=0)
|
|
56
57
|
downvotes: Mapped[int] = mapped_column(Integer, default=0)
|
|
@@ -72,7 +73,7 @@ class Program(Base):
|
|
|
72
73
|
class Alias(Base):
|
|
73
74
|
__tablename__ = "aliases"
|
|
74
75
|
|
|
75
|
-
slug: Mapped[str] = mapped_column(String(
|
|
76
|
+
slug: Mapped[str] = mapped_column(String(120), primary_key=True)
|
|
76
77
|
program_id: Mapped[str] = mapped_column(
|
|
77
78
|
Text, ForeignKey("programs.id"), nullable=False
|
|
78
79
|
)
|
|
@@ -10,7 +10,7 @@ from sqlalchemy import select
|
|
|
10
10
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
11
11
|
|
|
12
12
|
from api.dependencies import get_current_user
|
|
13
|
-
from api.models.database import get_db
|
|
13
|
+
from api.models.database import get_db, get_session_factory
|
|
14
14
|
from api.models.orm import CompileLog, Program, User
|
|
15
15
|
from api.models.schemas import CompileRequest, CompileResponse, CompileStatusResponse, CompileTimings
|
|
16
16
|
|
|
@@ -81,9 +81,9 @@ async def submit_compile(
|
|
|
81
81
|
"error": None,
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
# For now, run compilation synchronously (will switch to Celery for async)
|
|
85
84
|
import asyncio
|
|
86
85
|
from api.services.compile_service import SpecTooLongError, get_compile_service
|
|
86
|
+
from api.services.storage_service import upload_paw_to_hf, delete_lora_params
|
|
87
87
|
|
|
88
88
|
try:
|
|
89
89
|
service = get_compile_service()
|
|
@@ -119,6 +119,29 @@ async def submit_compile(
|
|
|
119
119
|
))
|
|
120
120
|
await db.commit()
|
|
121
121
|
|
|
122
|
+
# Background: upload .paw to HF + delete lora_params.pt
|
|
123
|
+
paw_path = result.get("paw_path")
|
|
124
|
+
|
|
125
|
+
async def _background_hf_upload():
|
|
126
|
+
try:
|
|
127
|
+
from pathlib import Path
|
|
128
|
+
if paw_path and Path(paw_path).exists():
|
|
129
|
+
hf_url = await asyncio.to_thread(upload_paw_to_hf, pid, paw_path)
|
|
130
|
+
async with get_session_factory()() as bg_db:
|
|
131
|
+
prog = await bg_db.get(Program, pid)
|
|
132
|
+
if prog:
|
|
133
|
+
prog.hf_url = hf_url
|
|
134
|
+
await bg_db.commit()
|
|
135
|
+
|
|
136
|
+
adapter_dir = Path(paw_path).parent if paw_path else None
|
|
137
|
+
if adapter_dir and adapter_dir.exists():
|
|
138
|
+
delete_lora_params(adapter_dir)
|
|
139
|
+
except Exception:
|
|
140
|
+
logger.exception("background_hf_upload_failed", program_id=pid)
|
|
141
|
+
|
|
142
|
+
if not result.get("cached"):
|
|
143
|
+
asyncio.create_task(_background_hf_upload())
|
|
144
|
+
|
|
122
145
|
t = result.get("timings", {})
|
|
123
146
|
def _ms(key: str) -> float | None:
|
|
124
147
|
v = t.get(key)
|
|
@@ -6,7 +6,7 @@ import re
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
9
|
-
from fastapi.responses import FileResponse
|
|
9
|
+
from fastapi.responses import FileResponse, RedirectResponse
|
|
10
10
|
from sqlalchemy import func, select, or_
|
|
11
11
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
12
12
|
|
|
@@ -14,23 +14,38 @@ from api.config import get_settings
|
|
|
14
14
|
from api.dependencies import get_current_user, require_user
|
|
15
15
|
from api.models.database import get_db
|
|
16
16
|
from api.models.orm import Alias, Case, Program, User, Vote
|
|
17
|
+
from api.services.storage_service import get_hf_url
|
|
17
18
|
|
|
18
19
|
router = APIRouter()
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
SLUG_PART_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,48}[a-z0-9]$")
|
|
22
|
+
SYSTEM_NAMESPACE = "programasweights"
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
async def resolve_program(id_or_slug: str, db: AsyncSession) -> Program | None:
|
|
24
|
-
"""Resolve a program by hash ID or alias slug.
|
|
26
|
+
"""Resolve a program by hash ID or alias slug.
|
|
27
|
+
Bare slugs (no /) are tried as programasweights/slug first."""
|
|
25
28
|
result = await db.execute(select(Program).where(Program.id == id_or_slug))
|
|
26
29
|
program = result.scalar_one_or_none()
|
|
27
30
|
if program:
|
|
28
31
|
return program
|
|
29
32
|
|
|
33
|
+
slug = id_or_slug
|
|
30
34
|
result = await db.execute(
|
|
31
|
-
select(Program).join(Alias).where(Alias.slug ==
|
|
35
|
+
select(Program).join(Alias).where(Alias.slug == slug)
|
|
32
36
|
)
|
|
33
|
-
|
|
37
|
+
program = result.scalar_one_or_none()
|
|
38
|
+
if program:
|
|
39
|
+
return program
|
|
40
|
+
|
|
41
|
+
if "/" not in slug:
|
|
42
|
+
full_slug = f"{SYSTEM_NAMESPACE}/{slug}"
|
|
43
|
+
result = await db.execute(
|
|
44
|
+
select(Program).join(Alias).where(Alias.slug == full_slug)
|
|
45
|
+
)
|
|
46
|
+
return result.scalar_one_or_none()
|
|
47
|
+
|
|
48
|
+
return None
|
|
34
49
|
|
|
35
50
|
|
|
36
51
|
# --- Programs CRUD ---
|
|
@@ -95,10 +110,16 @@ async def list_programs(
|
|
|
95
110
|
}
|
|
96
111
|
|
|
97
112
|
|
|
98
|
-
@router.get("/programs/resolve/{slug}")
|
|
113
|
+
@router.get("/programs/resolve/{slug:path}")
|
|
99
114
|
async def resolve_slug(slug: str, db: AsyncSession = Depends(get_db)):
|
|
100
115
|
result = await db.execute(select(Alias).where(Alias.slug == slug))
|
|
101
116
|
alias = result.scalar_one_or_none()
|
|
117
|
+
|
|
118
|
+
if not alias and "/" not in slug:
|
|
119
|
+
full_slug = f"{SYSTEM_NAMESPACE}/{slug}"
|
|
120
|
+
result = await db.execute(select(Alias).where(Alias.slug == full_slug))
|
|
121
|
+
alias = result.scalar_one_or_none()
|
|
122
|
+
|
|
102
123
|
if not alias:
|
|
103
124
|
raise HTTPException(status_code=404, detail={
|
|
104
125
|
"error": "alias_not_found",
|
|
@@ -134,6 +155,7 @@ async def get_program(id_or_slug: str, db: AsyncSession = Depends(get_db)):
|
|
|
134
155
|
"public": program.public,
|
|
135
156
|
"aliases": aliases,
|
|
136
157
|
"download_url": f"/api/v1/programs/{program.id}/download",
|
|
158
|
+
"hf_url": program.hf_url,
|
|
137
159
|
"created_at": program.created_at.isoformat() if program.created_at else None,
|
|
138
160
|
}
|
|
139
161
|
|
|
@@ -142,29 +164,25 @@ async def get_program(id_or_slug: str, db: AsyncSession = Depends(get_db)):
|
|
|
142
164
|
async def download_program(id_or_slug: str, db: AsyncSession = Depends(get_db)):
|
|
143
165
|
program = await resolve_program(id_or_slug, db)
|
|
144
166
|
if not program:
|
|
145
|
-
settings = get_settings()
|
|
146
|
-
paw_file = Path(settings.STORAGE_PATH) / id_or_slug / f"{id_or_slug}.paw"
|
|
147
|
-
if paw_file.exists():
|
|
148
|
-
return FileResponse(
|
|
149
|
-
path=str(paw_file),
|
|
150
|
-
media_type="application/octet-stream",
|
|
151
|
-
filename=f"{id_or_slug}.paw",
|
|
152
|
-
)
|
|
153
167
|
raise HTTPException(status_code=404, detail=f"Program '{id_or_slug}' not found")
|
|
154
168
|
|
|
155
169
|
program.downloads = (program.downloads or 0) + 1
|
|
156
170
|
await db.commit()
|
|
157
171
|
|
|
172
|
+
if program.hf_url:
|
|
173
|
+
return RedirectResponse(url=program.hf_url, status_code=302)
|
|
174
|
+
|
|
158
175
|
settings = get_settings()
|
|
159
176
|
paw_file = Path(settings.STORAGE_PATH) / program.id / f"{program.id}.paw"
|
|
160
|
-
if
|
|
161
|
-
|
|
177
|
+
if paw_file.exists():
|
|
178
|
+
return FileResponse(
|
|
179
|
+
path=str(paw_file),
|
|
180
|
+
media_type="application/octet-stream",
|
|
181
|
+
filename=f"{program.id}.paw",
|
|
182
|
+
)
|
|
162
183
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
media_type="application/octet-stream",
|
|
166
|
-
filename=f"{program.id}.paw",
|
|
167
|
-
)
|
|
184
|
+
hf_url = get_hf_url(program.id)
|
|
185
|
+
return RedirectResponse(url=hf_url, status_code=302)
|
|
168
186
|
|
|
169
187
|
|
|
170
188
|
# --- Voting ---
|
|
@@ -314,26 +332,29 @@ async def create_alias(
|
|
|
314
332
|
raise HTTPException(status_code=404, detail="Program not found")
|
|
315
333
|
|
|
316
334
|
body = await request.json()
|
|
317
|
-
|
|
335
|
+
raw_slug = body.get("slug", "").lower().strip()
|
|
318
336
|
|
|
319
|
-
if not
|
|
337
|
+
if not SLUG_PART_RE.match(raw_slug):
|
|
320
338
|
raise HTTPException(status_code=422, detail={
|
|
321
339
|
"error": "invalid_slug",
|
|
322
|
-
"message": "
|
|
340
|
+
"message": "Name must be 2-50 chars, lowercase alphanumeric and hyphens, "
|
|
323
341
|
"start and end with alphanumeric.",
|
|
324
342
|
})
|
|
325
343
|
|
|
326
|
-
|
|
344
|
+
username = user.username or f"user-{user.id}"
|
|
345
|
+
full_slug = f"{username}/{raw_slug}"
|
|
346
|
+
|
|
347
|
+
existing = await db.execute(select(Alias).where(Alias.slug == full_slug))
|
|
327
348
|
if existing.scalar_one_or_none():
|
|
328
349
|
raise HTTPException(status_code=409, detail={
|
|
329
350
|
"error": "slug_taken",
|
|
330
|
-
"message": f"
|
|
351
|
+
"message": f"You already have a program named '{raw_slug}'.",
|
|
331
352
|
})
|
|
332
353
|
|
|
333
|
-
alias = Alias(slug=
|
|
354
|
+
alias = Alias(slug=full_slug, program_id=program_id, owner_id=user.id)
|
|
334
355
|
db.add(alias)
|
|
335
356
|
await db.commit()
|
|
336
|
-
return {"slug":
|
|
357
|
+
return {"slug": full_slug, "program_id": program_id}
|
|
337
358
|
|
|
338
359
|
|
|
339
360
|
@router.delete("/programs/{program_id}/alias/{slug}")
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Storage service: upload .paw files to HuggingFace, manage local cleanup.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import time
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import structlog
|
|
10
|
+
|
|
11
|
+
logger = structlog.get_logger()
|
|
12
|
+
|
|
13
|
+
HF_REPO_ID = "yuntian-deng/paw-programs"
|
|
14
|
+
HF_BASE_URL = f"https://huggingface.co/{HF_REPO_ID}/resolve/main"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_hf_url(program_id: str) -> str:
|
|
18
|
+
return f"{HF_BASE_URL}/{program_id}.paw"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upload_paw_to_hf(program_id: str, paw_path: str) -> str:
|
|
22
|
+
"""Upload a .paw file to HuggingFace. Returns the download URL."""
|
|
23
|
+
from huggingface_hub import HfApi
|
|
24
|
+
|
|
25
|
+
api = HfApi()
|
|
26
|
+
url = get_hf_url(program_id)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
api.upload_file(
|
|
30
|
+
path_or_fileobj=paw_path,
|
|
31
|
+
path_in_repo=f"{program_id}.paw",
|
|
32
|
+
repo_id=HF_REPO_ID,
|
|
33
|
+
repo_type="model",
|
|
34
|
+
)
|
|
35
|
+
logger.info("hf_upload_success", program_id=program_id, url=url)
|
|
36
|
+
except Exception:
|
|
37
|
+
logger.exception("hf_upload_failed", program_id=program_id)
|
|
38
|
+
raise
|
|
39
|
+
|
|
40
|
+
return url
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def delete_lora_params(program_dir: Path) -> int:
|
|
44
|
+
"""Delete lora_params.pt if it exists. Returns bytes freed."""
|
|
45
|
+
pt_file = program_dir / "lora_params.pt"
|
|
46
|
+
if pt_file.exists():
|
|
47
|
+
size = pt_file.stat().st_size
|
|
48
|
+
pt_file.unlink()
|
|
49
|
+
logger.info("deleted_lora_params", dir=str(program_dir), freed_mb=round(size / 1024 / 1024, 1))
|
|
50
|
+
return size
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def cleanup_cold_programs(
|
|
55
|
+
storage_path: Path,
|
|
56
|
+
max_age_days: int = 30,
|
|
57
|
+
keep_peft: bool = True,
|
|
58
|
+
) -> dict:
|
|
59
|
+
"""Remove local files for programs that have HF backups and haven't been accessed recently.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
storage_path: Path to /data/programs
|
|
63
|
+
max_age_days: Programs not accessed in this many days are eligible
|
|
64
|
+
keep_peft: If True, keep adapter_model.safetensors for vLLM inference
|
|
65
|
+
"""
|
|
66
|
+
from api.models.database import get_session_factory
|
|
67
|
+
from api.models.orm import Program
|
|
68
|
+
from sqlalchemy import select
|
|
69
|
+
import asyncio
|
|
70
|
+
|
|
71
|
+
cutoff = time.time() - max_age_days * 86400
|
|
72
|
+
stats = {"checked": 0, "cleaned": 0, "bytes_freed": 0, "skipped_no_hf": 0}
|
|
73
|
+
|
|
74
|
+
async def _cleanup():
|
|
75
|
+
factory = get_session_factory()
|
|
76
|
+
async with factory() as db:
|
|
77
|
+
result = await db.execute(
|
|
78
|
+
select(Program).where(Program.hf_url.isnot(None))
|
|
79
|
+
)
|
|
80
|
+
programs = result.scalars().all()
|
|
81
|
+
|
|
82
|
+
for prog in programs:
|
|
83
|
+
stats["checked"] += 1
|
|
84
|
+
prog_dir = storage_path / prog.id
|
|
85
|
+
|
|
86
|
+
if not prog_dir.exists():
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
paw_file = prog_dir / f"{prog.id}.paw"
|
|
90
|
+
if not paw_file.exists():
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
last_access = paw_file.stat().st_atime
|
|
94
|
+
if last_access > cutoff:
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
for fname in ["lora_params.pt", f"{prog.id}.paw", "adapter.gguf"]:
|
|
98
|
+
f = prog_dir / fname
|
|
99
|
+
if f.exists():
|
|
100
|
+
stats["bytes_freed"] += f.stat().st_size
|
|
101
|
+
f.unlink()
|
|
102
|
+
|
|
103
|
+
if not keep_peft:
|
|
104
|
+
for fname in ["adapter_model.safetensors"]:
|
|
105
|
+
f = prog_dir / fname
|
|
106
|
+
if f.exists():
|
|
107
|
+
stats["bytes_freed"] += f.stat().st_size
|
|
108
|
+
f.unlink()
|
|
109
|
+
|
|
110
|
+
stats["cleaned"] += 1
|
|
111
|
+
logger.info("cleaned_program", program_id=prog.id)
|
|
112
|
+
|
|
113
|
+
asyncio.run(_cleanup())
|
|
114
|
+
return stats
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Cleanup script: delete lora_params.pt from all programs, optionally
|
|
4
|
+
clean cold programs that have HF backups.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
# Delete all lora_params.pt (always safe)
|
|
8
|
+
python scripts/cleanup_storage.py --delete-pt
|
|
9
|
+
|
|
10
|
+
# Clean cold programs (30+ days, have HF backup)
|
|
11
|
+
python scripts/cleanup_storage.py --clean-cold --max-age-days 30
|
|
12
|
+
|
|
13
|
+
# Both
|
|
14
|
+
python scripts/cleanup_storage.py --delete-pt --clean-cold
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def delete_all_lora_params(storage_path: Path):
|
|
25
|
+
"""Delete lora_params.pt from all program directories."""
|
|
26
|
+
total_freed = 0
|
|
27
|
+
count = 0
|
|
28
|
+
for d in storage_path.iterdir():
|
|
29
|
+
if not d.is_dir():
|
|
30
|
+
continue
|
|
31
|
+
pt_file = d / "lora_params.pt"
|
|
32
|
+
if pt_file.exists():
|
|
33
|
+
size = pt_file.stat().st_size
|
|
34
|
+
pt_file.unlink()
|
|
35
|
+
total_freed += size
|
|
36
|
+
count += 1
|
|
37
|
+
print(f"Deleted {count} lora_params.pt files, freed {total_freed / 1024 / 1024:.0f} MB")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main():
|
|
41
|
+
parser = argparse.ArgumentParser(description="PAW storage cleanup")
|
|
42
|
+
parser.add_argument("--storage-path", default="/data/programs")
|
|
43
|
+
parser.add_argument("--delete-pt", action="store_true",
|
|
44
|
+
help="Delete all lora_params.pt files (always safe)")
|
|
45
|
+
parser.add_argument("--clean-cold", action="store_true",
|
|
46
|
+
help="Clean cold programs with HF backups")
|
|
47
|
+
parser.add_argument("--max-age-days", type=int, default=30)
|
|
48
|
+
args = parser.parse_args()
|
|
49
|
+
|
|
50
|
+
storage = Path(args.storage_path)
|
|
51
|
+
if not storage.exists():
|
|
52
|
+
print(f"Storage path {storage} does not exist")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
if args.delete_pt:
|
|
56
|
+
delete_all_lora_params(storage)
|
|
57
|
+
|
|
58
|
+
if args.clean_cold:
|
|
59
|
+
from api.services.storage_service import cleanup_cold_programs
|
|
60
|
+
stats = cleanup_cold_programs(storage, max_age_days=args.max_age_days)
|
|
61
|
+
print(f"Cleanup: checked={stats['checked']}, cleaned={stats['cleaned']}, "
|
|
62
|
+
f"freed={stats['bytes_freed'] / 1024 / 1024:.0f} MB")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
main()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the storage + alias + HF upload system.
|
|
3
|
+
TDD: these tests define the expected behavior BEFORE implementation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
# ── Alias resolution ──
|
|
9
|
+
|
|
10
|
+
class TestAliasResolution:
|
|
11
|
+
"""Namespaced aliases: username/slug format with programasweights/ shorthand."""
|
|
12
|
+
|
|
13
|
+
async def test_bare_name_resolves_to_programasweights_namespace(self, client):
|
|
14
|
+
"""'email-triage' should resolve to programasweights/email-triage."""
|
|
15
|
+
resp = await client.get("/api/v1/programs/resolve/email-triage")
|
|
16
|
+
assert resp.status_code == 200
|
|
17
|
+
data = resp.json()
|
|
18
|
+
assert "program_id" in data
|
|
19
|
+
assert data["slug"] == "programasweights/email-triage"
|
|
20
|
+
|
|
21
|
+
async def test_namespaced_slug_resolves(self, client):
|
|
22
|
+
"""'da03/my-program' should resolve to the correct program."""
|
|
23
|
+
resp = await client.get("/api/v1/programs/resolve/da03/my-program")
|
|
24
|
+
assert resp.status_code == 200
|
|
25
|
+
data = resp.json()
|
|
26
|
+
assert "program_id" in data
|
|
27
|
+
|
|
28
|
+
async def test_unknown_slug_returns_404(self, client):
|
|
29
|
+
resp = await client.get("/api/v1/programs/resolve/nonexistent/nope")
|
|
30
|
+
assert resp.status_code == 404
|
|
31
|
+
|
|
32
|
+
async def test_create_alias_requires_auth(self, client):
|
|
33
|
+
resp = await client.post(
|
|
34
|
+
"/api/v1/programs/some_program_id/alias",
|
|
35
|
+
json={"slug": "my-cool-program"},
|
|
36
|
+
)
|
|
37
|
+
assert resp.status_code == 401
|
|
38
|
+
|
|
39
|
+
async def test_create_alias_adds_username_prefix(self, client, auth_headers):
|
|
40
|
+
"""Creating alias 'my-prog' as user 'da03' should store 'da03/my-prog'."""
|
|
41
|
+
resp = await client.post(
|
|
42
|
+
"/api/v1/programs/some_program_id/alias",
|
|
43
|
+
json={"slug": "my-prog"},
|
|
44
|
+
headers=auth_headers,
|
|
45
|
+
)
|
|
46
|
+
assert resp.status_code in (200, 201)
|
|
47
|
+
data = resp.json()
|
|
48
|
+
assert data["slug"] == "da03/my-prog"
|
|
49
|
+
|
|
50
|
+
async def test_duplicate_slug_returns_409(self, client, auth_headers):
|
|
51
|
+
"""Same user can't create the same alias twice."""
|
|
52
|
+
await client.post(
|
|
53
|
+
"/api/v1/programs/some_program_id/alias",
|
|
54
|
+
json={"slug": "unique-name"},
|
|
55
|
+
headers=auth_headers,
|
|
56
|
+
)
|
|
57
|
+
resp = await client.post(
|
|
58
|
+
"/api/v1/programs/some_program_id/alias",
|
|
59
|
+
json={"slug": "unique-name"},
|
|
60
|
+
headers=auth_headers,
|
|
61
|
+
)
|
|
62
|
+
assert resp.status_code == 409
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ── Download redirect ──
|
|
66
|
+
|
|
67
|
+
class TestDownloadRedirect:
|
|
68
|
+
"""Download endpoint should redirect to HuggingFace URL."""
|
|
69
|
+
|
|
70
|
+
async def test_download_redirects_to_hf(self, client):
|
|
71
|
+
"""GET /programs/{id}/download should return 302 to HF URL."""
|
|
72
|
+
resp = await client.get(
|
|
73
|
+
"/api/v1/programs/9b57fd6fccf77885400e/download",
|
|
74
|
+
follow_redirects=False,
|
|
75
|
+
)
|
|
76
|
+
assert resp.status_code in (302, 307)
|
|
77
|
+
location = resp.headers.get("location", "")
|
|
78
|
+
assert "huggingface.co" in location
|
|
79
|
+
assert "9b57fd6fccf77885400e" in location
|
|
80
|
+
|
|
81
|
+
async def test_download_by_slug_redirects(self, client):
|
|
82
|
+
"""GET /programs/email-triage/download should also redirect."""
|
|
83
|
+
resp = await client.get(
|
|
84
|
+
"/api/v1/programs/email-triage/download",
|
|
85
|
+
follow_redirects=False,
|
|
86
|
+
)
|
|
87
|
+
assert resp.status_code in (302, 307)
|
|
88
|
+
|
|
89
|
+
async def test_download_unknown_returns_404(self, client):
|
|
90
|
+
resp = await client.get("/api/v1/programs/nonexistent_id/download")
|
|
91
|
+
assert resp.status_code == 404
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ── HF upload ──
|
|
95
|
+
|
|
96
|
+
class TestHFUpload:
|
|
97
|
+
"""After compile, .paw should be uploaded to HuggingFace."""
|
|
98
|
+
|
|
99
|
+
async def test_compile_sets_hf_url(self, client, auth_headers):
|
|
100
|
+
"""After successful compile, program should have hf_url set in DB."""
|
|
101
|
+
resp = await client.post(
|
|
102
|
+
"/api/v1/compile",
|
|
103
|
+
json={"spec": "Classify text as spam or not spam"},
|
|
104
|
+
headers=auth_headers,
|
|
105
|
+
)
|
|
106
|
+
if resp.status_code == 202:
|
|
107
|
+
data = resp.json()
|
|
108
|
+
if data["status"] == "ready":
|
|
109
|
+
program_id = data["program_id"]
|
|
110
|
+
prog_resp = await client.get(f"/api/v1/programs/{program_id}")
|
|
111
|
+
prog_data = prog_resp.json()
|
|
112
|
+
assert prog_data.get("hf_url") is not None
|
|
113
|
+
assert "huggingface.co" in prog_data["hf_url"]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# ── Cleanup ──
|
|
117
|
+
|
|
118
|
+
class TestCleanup:
|
|
119
|
+
"""Cleanup should remove local files for cold programs that have HF backups."""
|
|
120
|
+
|
|
121
|
+
def test_lora_params_never_needed(self):
|
|
122
|
+
"""lora_params.pt should never exist after compile+cleanup."""
|
|
123
|
+
import os
|
|
124
|
+
from pathlib import Path
|
|
125
|
+
programs_dir = Path("/data/programs")
|
|
126
|
+
if programs_dir.exists():
|
|
127
|
+
for d in programs_dir.iterdir():
|
|
128
|
+
pt_file = d / "lora_params.pt"
|
|
129
|
+
if pt_file.exists():
|
|
130
|
+
size_mb = pt_file.stat().st_size / 1024 / 1024
|
|
131
|
+
pytest.fail(
|
|
132
|
+
f"lora_params.pt still exists in {d.name} ({size_mb:.0f} MB). "
|
|
133
|
+
"Should be deleted after GGUF conversion."
|
|
134
|
+
)
|