diff --git a/.github/workflows/rust-interpreter.yml b/.github/workflows/rust-interpreter.yml index f9827e813..2d05d439d 100644 --- a/.github/workflows/rust-interpreter.yml +++ b/.github/workflows/rust-interpreter.yml @@ -30,7 +30,9 @@ jobs: npm ci & sudo apt update && sudo apt install -y libgtk-3-dev libwebkit2gtk-4.1-dev librsvg2-dev patchelf at-spi2-core & (curl -fsSL https://ollama.com/install.sh | sudo -E sh && sleep 2) wait - # todo: do this in rust - ollama pull granite3.2:2b - name: Run interpreter tests - run: npm run test:interpreter + run: | + python3.12 -mvenv venv + source venv/bin/activate + pip install nested-diff + npm run test:interpreter diff --git a/.github/workflows/tauri-cli.yml b/.github/workflows/tauri-cli.yml index 30a9c25ea..47886a006 100644 --- a/.github/workflows/tauri-cli.yml +++ b/.github/workflows/tauri-cli.yml @@ -31,27 +31,20 @@ jobs: sudo apt install -y libgtk-3-dev libwebkit2gtk-4.1-dev librsvg2-dev patchelf at-spi2-core && \ (curl -fsSL https://ollama.com/install.sh | sudo -E sh && sleep 2) wait - - name: Test production build - run: npm run tauri build -- --bundles deb # Skip testing appimage, is this dangerous? It's slow... + - name: Test production build # Skip testing appimage, is this dangerous? It's slow... + run: | + npm run tauri build -- --ci --bundles deb + - name: Install production build + run: | + ls ./src-tauri/target/release/bundle/deb && sudo apt install -y ./src-tauri/target/release/bundle/deb/*.deb - name: Setup xvfb for screen 0 - run: Xvfb :1 -screen 0 1600x1200x24 & - - - name: Test beeai compiler - env: - DISPLAY: :1 run: | - PATH=./src-tauri/target/release/:$PATH - - for i in ./demos/beeai/*.py - do pdl compile beeai $i -g -o /tmp/z.json && jq .description /tmp/z.json - done + Xvfb :1 -screen 0 1600x1200x24 & - - name: Test pdl run against production build + - name: Test 'pdl run' against production build env: DISPLAY: :1 run: | - PATH=./src-tauri/target/release/:$PATH - # 1a. `run` subcommand errors due to missing required positional parameter pdl run && (echo "This should have failed" && exit 1) || (echo "Great, expected failure received" && exit 0) @@ -69,9 +62,9 @@ jobs: # demo5,demo6 each depend on an external file, and the interpreter does not currently capture this in the trace # demo8 currently requires building a model which the interpreter does not directly support # demo9 takes forever, so... for now skip it - for i in ./src/demos/*.json - do if [[ $(basename $i) != "demo4.json" ]] && [[ $(basename $i) != "demo5.json" ]] && [[ $(basename $i) != "demo6.json" ]] && [[ $(basename $i) != "demo8.json" ]] && [[ $(basename $i) != "demo9.json" ]]; then pdl run $i; fi - done + #for i in ./src/demos/*.json + #do if [[ $(basename $i) != "demo4.json" ]] && [[ $(basename $i) != "demo5.json" ]] && [[ $(basename $i) != "demo6.json" ]] && [[ $(basename $i) != "demo8.json" ]] && [[ $(basename $i) != "demo9.json" ]]; then pdl run $i; fi + #done - name: Tear down xvfb run: killall Xvfb || true diff --git a/examples/callback/repair_prompt.pdl b/examples/callback/repair_prompt.pdl index bc795842e..2bc6796b5 100644 --- a/examples/callback/repair_prompt.pdl +++ b/examples/callback/repair_prompt.pdl @@ -11,7 +11,6 @@ lastOf: - def: raw_output model: ollama_chat/granite3.2:2b parameters: - #stop_sequences: "\n\n" temperature: 0 - lang: python diff --git a/examples/cldk/cldk-assistant.pdl b/examples/cldk/cldk-assistant.pdl index 339790d0d..0253724e8 100644 --- a/examples/cldk/cldk-assistant.pdl +++ b/examples/cldk/cldk-assistant.pdl @@ -130,7 +130,7 @@ text: parameters: - stop_sequences: "Question" + stop: ["Question"] temperature: 0 - "\n\n***Executing the above PDL code:\n\n" - lang: python diff --git a/examples/demo/10-sdg.pdl b/examples/demo/10-sdg.pdl index 68dbe11db..ac1ba52ea 100644 --- a/examples/demo/10-sdg.pdl +++ b/examples/demo/10-sdg.pdl @@ -68,7 +68,7 @@ defs: input: ${teacher_input} parameters: temperature: 0 - stop_sequences: "${teacher_stop_token}" + stop: ["${teacher_stop_token}"] max_new_tokens: ${prompt_data.max_new_tokens} parser: regex: '### Question [0-9]+:\s*([^#\n]+)' @@ -156,7 +156,7 @@ defs: model: ${teacher_model} input: ${teacher_input} parameters: - stop_sequences: "${teacher_stop_token}" + stop: ["${teacher_stop_token}"] max_new_tokens: ${prompt_data.max_new_tokens} temperature: 0 parser: @@ -252,7 +252,7 @@ defs: model: ${teacher_model} input: ${teacher_input} parameters: - stop_sequences: ${ ([teacher_stop_token] + prompt_data.additional_stop_tokens) | join(',') } + stop: ["${ ([teacher_stop_token] + prompt_data.additional_stop_tokens) | join(',') }"] max_new_tokens: ${prompt_data.max_new_tokens} temperature: 0 parsed_answer: @@ -339,7 +339,7 @@ defs: model: ${teacher_model} input: ${teacher_input} parameters: - stop_sequences: "${teacher_stop_token}" + stop: ["${teacher_stop_token}"] max_new_tokens: ${prompt_data.max_new_tokens} temperature: 0 parser: diff --git a/examples/gsm8k/gsm8k-tot-few-shot.pdl b/examples/gsm8k/gsm8k-tot-few-shot.pdl new file mode 100644 index 000000000..37a2e7f4c --- /dev/null +++ b/examples/gsm8k/gsm8k-tot-few-shot.pdl @@ -0,0 +1,221 @@ +description: Grade School Math -- for every problem we generate a plan, then exectute and evaluate it. +defs: + problems: + read: ./test.jsonl + parser: jsonl + + MAX_ITERATIONS: 10 + N: 3 + + + majority_vote: + function: + numbers: [float] + return: + lang: python + code: | + from collections import Counter + frequency = Counter( ${ numbers }) + most_frequent = max(frequency, key=frequency.get) + result = most_frequent + + majority_vote_json: + function: + results: [{ "result": float }] + return: + lastOf: + - lang: python + def: numbers + code: | + result = [o["result"] for o in ${ results }] + - call: ${ majority_vote } + args: + numbers: ${ numbers } + + planning: + function: + problem: str + demos: [str] + return: + text: + - | + Please generate a high-level plan for solving the following question. + As the first step, just say what method and idea you will use to solve the question. + You can reorganize the information in the question. Do not do the actual calculation. + Keep your response concise and within 80 words. + + - for: + demo: ${ demos } + repeat: + ${ demo } + join: + with: "\n" + - text: + - "\nProblem:\n" + - ${ problem } + - "\n" + - model: ollama/granite3.2:8b + parameters: + temperature: 0.7 + top_p: 0.85 + + solve: + function: + plan: str + return: + text: + - ${ plan } + - | + + The plan looks good! Now, use real numbers and do the calculation. Please solve the question + step-by-step according to the high-level plan. Give me the final answer. Make your response short. + - "\nThe answer is:\n" + - model: ollama/granite3.2:8b + parameters: + temperature: 0.7 + top_p: 0.85 + + extract_final_answer: + function: + solution: str + return: + lastOf: + - ${ solution } + - Extract the result from the above solution into a JSON object with field "result" and a float as value. Remove any dollar signs or other symbols. + - model: ollama/granite3.2:8b + parser: json + def: result + spec: { "result": float } + fallback: + data: + result: 0 + + compare_to_ground_truth: + function: + result: float + truth: str + return: + lastOf: + - data: ${ truth } + parser: + regex: "(.|\n)*#### (?P([0-9])*)\n*" + spec: + answer: str + def: ground_truth + - if: ${ result|float == ground_truth.answer|float} + then: + 1 + else: + 0 + +text: +- defs: + demos: + read: demos.yaml + parser: yaml + for: + problem: ${ problems } + repeat: + repeat: + call: ${ planning } + args: + pdl_context: [] + problem: ${ problem.question } + demos: ${ demos } + max_iterations: ${ N } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: plans + join: + as: array + +- for: + plans_for_problem: ${ plans } + repeat: + for: + plan: ${ plans_for_problem } + repeat: + repeat: + call: ${ solve } + args: + pdl_context: [] + plan: ${ plan } + max_iterations: ${ N } + join: + as: array + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: solutions + join: + as: array + +- for: + solution: ${ solutions } + repeat: + for: + solutions_for_problem: ${ solution } + repeat: + for: + solution_for_problem: ${ solutions_for_problem } + repeat: + call: ${ extract_final_answer } + args: + pdl_context: [] + solution: ${ solution_for_problem } + max_iterations: ${ N } + join: + as: array + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: results + join: + as: array + +- for: + all_results_for_problem: ${ results } + repeat: + for: + results_for_problem: ${ all_results_for_problem } + repeat: + call: ${ majority_vote_json } + args: + pdl_context: [] + results: ${ results_for_problem } + max_iterations: ${ N } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: per_plan_votes + join: + as: array + +- for: + votes: ${ per_plan_votes } + repeat: + call: ${ majority_vote } + args: + pdl_context: [] + numbers: ${ votes } + max_iterations: ${ MAX_ITERATIONS } + join: + as: array + def: results + +- for: + result: ${ results } + problem: ${ problems[:MAX_ITERATIONS] } + repeat: + call: ${ compare_to_ground_truth } + args: + pdl_context: [] + result: ${ result } + truth: ${ problem.answer } + max_iterations: ${ MAX_ITERATIONS } + def: stats + join: + as: array + +- "\nAccuracy: ${ stats|sum / MAX_ITERATIONS * 100}% " \ No newline at end of file diff --git a/examples/gsm8k/gsm8k-tot-multiplan.pdl b/examples/gsm8k/gsm8k-tot-multiplan.pdl new file mode 100644 index 000000000..321aea6d5 --- /dev/null +++ b/examples/gsm8k/gsm8k-tot-multiplan.pdl @@ -0,0 +1,167 @@ +description: Grade School Math -- for every problem we generate a plan, then exectute and evaluate it. +defs: + problems: + read: ./test.jsonl + parser: jsonl + + MAX_ITERATIONS: 2 + N: 3 + + + majority_vote: + function: + results: [{ "result": float }] + return: + lang: python + code: | + from collections import Counter + numbers = [o["result"] for o in ${ results }] + frequency = Counter(numbers) + most_frequent = max(frequency, key=frequency.get) + result = most_frequent + planning: + function: + problem: str + return: + text: + - | + Please generate a high-level plan for solving the following question. + As the first step, just say what method and idea you will use to solve the question. + You can reorganize the information in the question. Do not do the actual calculation. + Keep your response concise and within 80 words. + + - "\nProblem:\n" + - ${ problem } + - "\n" + - model: ollama/granite3.2:8b + parameters: + temperature: 0.7 + top_p: 0.85 + + solve: + function: + plan: str + return: + text: + - ${ plan } + - | + + The plan looks good! Now, use real numbers and do the calculation. Please solve the question + step-by-step according to the high-level plan. Give me the final answer. Make your response short. + - "\nThe answer is:\n" + - model: ollama/granite3.2:8b + parameters: + temperature: 0.7 + top_p: 0.85 + + extract_final_answer: + function: + solution: str + return: + lastOf: + - ${ solution } + - Extract the result from the above solution into a JSON object with field "result" and a float as value. Remove any dollar signs or other symbols. + - model: ollama/granite3.2:8b + parser: json + def: result + spec: { "result": float } + fallback: + data: + result: 0 + + compare_to_ground_truth: + function: + result: float + truth: str + return: + lastOf: + - data: ${ truth } + parser: + regex: "(.|\n)*#### (?P([0-9])*)\n*" + spec: + answer: str + def: ground_truth + - if: ${ result|float == ground_truth.answer|float} + then: + 1 + else: + 0 + +text: +- for: + problem: ${ problems } + repeat: + repeat: + call: ${ planning } + args: + pdl_context: [] + problem: ${ problem.question } + max_iterations: ${ N } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: plans + join: + as: array + +- for: + plans_for_problem: ${ plans } + repeat: + for: + plan: ${ plans_for_problem } + repeat: + call: ${ solve } + args: + pdl_context: [] + plan: ${ plan } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: solutions + join: + as: array + +- for: + solution: ${ solutions } + repeat: + for: + solution_for_problem: ${ solution } + repeat: + call: ${ extract_final_answer } + args: + pdl_context: [] + solution: ${ solution_for_problem } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: results + join: + as: array + +- for: + results_for_problem: ${ results } + repeat: + call: ${ majority_vote } + args: + pdl_context: [] + results: ${ results_for_problem } + max_iterations: ${ MAX_ITERATIONS } + def: votes + join: + as: array + +- for: + result: ${ votes } + problem: ${ problems[:MAX_ITERATIONS] } + repeat: + call: ${ compare_to_ground_truth } + args: + pdl_context: [] + result: ${ result } + truth: ${ problem.answer } + max_iterations: ${ MAX_ITERATIONS } + def: stats + join: + as: array + +- "\nAccuracy: ${ stats|sum / MAX_ITERATIONS * 100}% " \ No newline at end of file diff --git a/examples/gsm8k/gsm8k-tot.pdl b/examples/gsm8k/gsm8k-tot.pdl new file mode 100644 index 000000000..d9df68868 --- /dev/null +++ b/examples/gsm8k/gsm8k-tot.pdl @@ -0,0 +1,208 @@ +description: Grade School Math -- for every problem we generate a plan, then exectute and evaluate it. +defs: + problems: + read: ./test.jsonl + parser: jsonl + + MAX_ITERATIONS: 10 + N: 3 + + + majority_vote: + function: + numbers: [float] + return: + lang: python + code: | + from collections import Counter + frequency = Counter( ${ numbers }) + most_frequent = max(frequency, key=frequency.get) + result = most_frequent + + majority_vote_json: + function: + results: [{ "result": float }] + return: + lastOf: + - lang: python + def: numbers + code: | + result = [o["result"] for o in ${ results }] + - call: ${ majority_vote } + args: + numbers: ${ numbers } + + planning: + function: + problem: str + return: + text: + - | + Please generate a high-level plan for solving the following question. + As the first step, just say what method and idea you will use to solve the question. + You can reorganize the information in the question. Do not do the actual calculation. + Keep your response concise and within 80 words. + + - "\nProblem:\n" + - ${ problem } + - "\n" + - model: ollama/granite3.2:8b + parameters: + temperature: 0.7 + top_p: 0.85 + + solve: + function: + plan: str + return: + text: + - ${ plan } + - | + + The plan looks good! Now, use real numbers and do the calculation. Please solve the question + step-by-step according to the high-level plan. Give me the final answer. Make your response short. + - "\nThe answer is:\n" + - model: ollama/granite3.2:8b + parameters: + temperature: 0.7 + top_p: 0.85 + + extract_final_answer: + function: + solution: str + return: + lastOf: + - ${ solution } + - Extract the result from the above solution into a JSON object with field "result" and a float as value. Remove any dollar signs or other symbols. + - model: ollama/granite3.2:8b + parser: json + def: result + spec: { "result": float } + fallback: + data: + result: 0 + + compare_to_ground_truth: + function: + result: float + truth: str + return: + lastOf: + - data: ${ truth } + parser: + regex: "(.|\n)*#### (?P([0-9])*)\n*" + spec: + answer: str + def: ground_truth + - if: ${ result|float == ground_truth.answer|float} + then: + 1 + else: + 0 + +text: +- for: + problem: ${ problems } + repeat: + repeat: + call: ${ planning } + args: + pdl_context: [] + problem: ${ problem.question } + max_iterations: ${ N } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: plans + join: + as: array + +- for: + plans_for_problem: ${ plans } + repeat: + for: + plan: ${ plans_for_problem } + repeat: + repeat: + call: ${ solve } + args: + pdl_context: [] + plan: ${ plan } + max_iterations: ${ N } + join: + as: array + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: solutions + join: + as: array + +- for: + solution: ${ solutions } + repeat: + for: + solutions_for_problem: ${ solution } + repeat: + for: + solution_for_problem: ${ solutions_for_problem } + repeat: + call: ${ extract_final_answer } + args: + pdl_context: [] + solution: ${ solution_for_problem } + max_iterations: ${ N } + join: + as: array + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: results + join: + as: array + +- for: + all_results_for_problem: ${ results } + repeat: + for: + results_for_problem: ${ all_results_for_problem } + repeat: + call: ${ majority_vote_json } + args: + pdl_context: [] + results: ${ results_for_problem } + max_iterations: ${ N } + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: per_plan_votes + join: + as: array + +- for: + votes: ${ per_plan_votes } + repeat: + call: ${ majority_vote } + args: + pdl_context: [] + numbers: ${ votes } + max_iterations: ${ MAX_ITERATIONS } + join: + as: array + def: results + +- for: + result: ${ results } + problem: ${ problems[:MAX_ITERATIONS] } + repeat: + call: ${ compare_to_ground_truth } + args: + pdl_context: [] + result: ${ result } + truth: ${ problem.answer } + max_iterations: ${ MAX_ITERATIONS } + def: stats + join: + as: array + +- "\nAccuracy: ${ stats|sum / MAX_ITERATIONS * 100}% " \ No newline at end of file diff --git a/examples/notebooks/demo.ipynb b/examples/notebooks/demo.ipynb index 87b0f63be..251f99fb9 100644 --- a/examples/notebooks/demo.ipynb +++ b/examples/notebooks/demo.ipynb @@ -347,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "dfef7096-b7a6-4966-8356-a306e701974b", "metadata": { "scrolled": true @@ -395,12 +395,12 @@ " - def: thought\n", " model: replicate/ibm-granite/granite-3.1-8b-instruct\n", " parameters:\n", - " stop_sequences: \"Act:\"\n", + " stop: [\"Act:\"]\n", " temperature: 0\n", " - def: rawAction\n", " model: replicate/ibm-granite/granite-3.1-8b-instruct\n", " parameters:\n", - " stop_sequences: \"\\n\"\n", + " stop: [\"\\n\"]\n", " temperature: 0\n", " - def: action\n", " lang: python\n", diff --git a/examples/notebooks/notebook.ipynb b/examples/notebooks/notebook.ipynb index d384c5dde..03586e617 100644 --- a/examples/notebooks/notebook.ipynb +++ b/examples/notebooks/notebook.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "f3c62df1-0347-4711-acd7-3892cfd5df30", "metadata": {}, "outputs": [ @@ -32,7 +32,7 @@ "- \"Hello\\n\"\n", "- model: \"replicate/ibm-granite/granite-3.1-8b-instruct\"\n", " parameters:\n", - " stop_sequences: \"!\"\n", + " stop: [\"!\"]\n", " " ] }, diff --git a/examples/notebooks/notebook_debug.ipynb b/examples/notebooks/notebook_debug.ipynb index 0287f66a0..b1b45accc 100644 --- a/examples/notebooks/notebook_debug.ipynb +++ b/examples/notebooks/notebook_debug.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "f3c62df1-0347-4711-acd7-3892cfd5df30", "metadata": {}, "outputs": [ @@ -166,7 +166,7 @@ "- Hello,\n", "- model: \"replicate/ibm-granite/granite-3.1-8b-instruct\"\n", " parameters:\n", - " stop_sequences: \"!\"" + " stop: [\"!\"]" ] }, { diff --git a/examples/notebooks/pdl.ipynb b/examples/notebooks/pdl.ipynb index 461415e4e..e644721e5 100644 --- a/examples/notebooks/pdl.ipynb +++ b/examples/notebooks/pdl.ipynb @@ -46,7 +46,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "f3c62df1-0347-4711-acd7-3892cfd5df30", "metadata": {}, "outputs": [ @@ -75,7 +75,7 @@ "- \"What is the meaning of life?\\n\"\n", "- model: replicate/ibm-granite/granite-3.2-8b-instruct\n", " parameters:\n", - " stop_sequences: \"!\"\n", + " stop: [\"!\"]\n", " include_stop_sequence: true" ] }, @@ -153,7 +153,7 @@ ] }, { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ ">>> What is APR?\n" @@ -169,7 +169,7 @@ ] }, { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ ">>> say it like a poem\n" @@ -200,7 +200,7 @@ ] }, { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ ">>> quit\n" diff --git a/examples/rag/rag.py b/examples/rag/rag.py index 0e81fae89..d4f89a067 100644 --- a/examples/rag/rag.py +++ b/examples/rag/rag.py @@ -105,7 +105,7 @@ def rag_retrieve( # Typically litellm.exceptions.APIConnectionError return f"Unexpected {type(be)}: be={be}" - data = response.data[0]["embedding"] + data = response.data[0]["embedding"] # type: ignore milvus_client = get_or_create_client(database_name) search_res = milvus_client.search( diff --git a/examples/rag/rag_library1.pdl b/examples/rag/rag_library1.pdl index 5b5eaca0b..3e81fd224 100644 --- a/examples/rag/rag_library1.pdl +++ b/examples/rag/rag_library1.pdl @@ -10,8 +10,8 @@ text: return: lang: python code: | - from examples.rag.rag import parse - result = parse(filename, chunk_size, chunk_overlap) + import rag + result = rag.parse(filename, chunk_size, chunk_overlap) - def: rag_index function: inp: list # This is a list[str], but PDL doesn't allow that type @@ -22,8 +22,8 @@ text: return: lang: python code: | - from examples.rag.rag import rag_index - result = rag_index(inp, encoder_model, embed_dimension, database_name, collection_name) + import rag + result = rag.rag_index(inp, encoder_model, embed_dimension, database_name, collection_name) - def: rag_retrieve function: inp: str @@ -34,5 +34,5 @@ text: return: lang: python code: | - from examples.rag.rag import rag_retrieve - result = rag_retrieve(inp, encoder_model, limit, database_name, collection_name) + import rag + result = rag.rag_retrieve(inp, encoder_model, limit, database_name, collection_name) diff --git a/examples/sdk/hello_dict.py b/examples/sdk/hello_dict.py index b83c22493..4313898ce 100644 --- a/examples/sdk/hello_dict.py +++ b/examples/sdk/hello_dict.py @@ -4,8 +4,10 @@ "text": [ "Hello\n", { - "model": "ollama_chat/granite3.2:8b", - "parameters": {"stop_sequences": "!"}, + "model": "ollama_chat/granite3.2:2b", + "parameters": { + "stop": ["!"], + }, }, ] } diff --git a/examples/sdk/hello_prog.py b/examples/sdk/hello_prog.py index f33718656..de0c8b8e8 100644 --- a/examples/sdk/hello_prog.py +++ b/examples/sdk/hello_prog.py @@ -6,8 +6,8 @@ text=[ "Hello\n", LitellmModelBlock( - model="ollama_chat/granite3.2:8b", - parameters=LitellmParameters(stop_sequences="!"), # pyright: ignore + model="ollama_chat/granite3.2:2b", + parameters=LitellmParameters(stop=["!"]), # pyright: ignore ), ] ) diff --git a/examples/sdk/hello_str.py b/examples/sdk/hello_str.py index b0711587e..8a6b52c90 100644 --- a/examples/sdk/hello_str.py +++ b/examples/sdk/hello_str.py @@ -3,9 +3,9 @@ HELLO = """ text: - "Hello\n" -- model: ollama_chat/granite3.2:8b +- model: ollama_chat/granite3.2:2b parameters: - stop_sequences: '!' + stop: ['!'] """ diff --git a/examples/skeleton-of-thought/tips.pdl b/examples/skeleton-of-thought/tips.pdl new file mode 100644 index 000000000..e3e9e367d --- /dev/null +++ b/examples/skeleton-of-thought/tips.pdl @@ -0,0 +1,89 @@ +description: Tip suggestion program illustration skeleton-of-thought +defs: + MAX_ITERATIONS: 50 + topics: + read: topic.jsonl + parser: jsonl + + expand_tip: + function: + topic: str + tip: str + return: + lastOf: + - | + Please expand a tip for a topic into a detailed paragraph. + Topic: staying healthy + Tip: Regular Exercise + Paragraph: Incorporate physical activity into your daily routine. This doesn't necessarily mean intense gym workouts; it can be as simple as walking, cycling, or yoga. Regular exercise helps in maintaining a healthy weight, improves cardiovascular health, boosts mental health, and can enhance cognitive function, which is crucial for fields that require intense intellectual engagement. + + Topic: building a campfire + Tip: Choose the Right Location + Paragraph: Always build your campfire in a safe spot. This means selecting a location that's away from trees, bushes, and other flammable materials. Ideally, use a fire ring if available. If you're building a fire pit, it should be on bare soil or on a bed of stones, not on grass or near roots which can catch fire underground. Make sure the area above is clear of low-hanging branches. + + Topic: writing a blog post + Tip: structure your content effectively + Paragraph: A well-structured post is easier to read and more enjoyable. Start with an engaging introduction that hooks the reader and clearly states the purpose of your post. Use headings and subheadings to break up the text and guide readers through your content. Bullet points and numbered lists can make information more digestible. Ensure each paragraph flows logically into the next, and conclude with a summary or call-to-action that encourages reader engagement. + + Topic: ${ topic } + Tip: ${ tip } + Paragraph: + + - model: ollama_chat/granite3.2:8b + parameters: + temperature: 0 + max_tokens: 128 + stop: [".\n\n"] + + suggest_tips: + function: + topic: str + number: int + return: + lastOf: + - "Please act as a helpful assistant. Your job is to provide users with useful tips on a specific topic.\n" + - "Give ${ number } tips for ${ topic }, each under 8 words. Do not use quotes. Make sure there are ${ number } tips.\n" + - model: ollama_chat/granite3.2:8b + parameters: + temperature: 0 + +text: +- for: + topic: ${ topics } + repeat: + call: ${ suggest_tips } + args: + topic: ${ topic.topic } + number: ${ topic.number } + pdl_context: [] + join: + as: array + max_iterations: ${ MAX_ITERATIONS } + def: skeletons +- "\n\n" +- for: + skeleton: ${ skeletons } + repeat: + lang: python + code: | + skel = """ ${skeleton} """ + result = skel.split("\n") + join: + as: array + def: skeletons_array +- for: + skeleton: ${ skeletons_array } + topic: ${ topics[:MAX_ITERATIONS] } + repeat: + for: + tip: ${ skeleton } + repeat: + call: ${ expand_tip } + args: + topic: ${ topic.topic } + tip: ${ tip } + pdl_context: [] + join: + as: array + + \ No newline at end of file diff --git a/examples/skeleton-of-thought/topic.jsonl b/examples/skeleton-of-thought/topic.jsonl new file mode 100644 index 000000000..c8ac6d12e --- /dev/null +++ b/examples/skeleton-of-thought/topic.jsonl @@ -0,0 +1,50 @@ +{"topic": "organizing a successful charity event", "number": 6} +{"topic": "improving personal credit scores", "number": 7} +{"topic": "staying motivated during job searches", "number": 5} +{"topic": "maintaining a work-life balance", "number": 9} +{"topic": "reducing carbon footprint at home", "number": 8} +{"topic": "starting a book club", "number": 5} +{"topic": "learning to play a musical instrument", "number": 7} +{"topic": "getting into freelance writing", "number": 6} +{"topic": "beginner yoga poses", "number": 8} +{"topic": "preparing for graduate school exams", "number": 5} +{"topic": "exploring minimalist living", "number": 9} +{"topic": "effective grocery shopping", "number": 7} +{"topic": "winter camping", "number": 5} +{"topic": "starting a podcast on a budget", "number": 8} +{"topic": "creating a capsule wardrobe", "number": 6} +{"topic": "improving your writing skills", "number": 7} +{"topic": "learning a new software quickly", "number": 9} +{"topic": "reducing anxiety before public speaking", "number": 5} +{"topic": "planning a solo travel adventure", "number": 8} +{"topic": "beginner skateboarders", "number": 6} +{"topic": "studying abroad", "number": 7} +{"topic": "planting a vegetable garden", "number": 5} +{"topic": "adopting a shelter pet", "number": 9} +{"topic": "learning to cook ethnic cuisines", "number": 8} +{"topic": "effective conflict resolution", "number": 5} +{"topic": "starting a vlog", "number": 7} +{"topic": "keeping a daily journal", "number": 6} +{"topic": "improving sleep hygiene", "number": 8} +{"topic": "beginner mountain climbers", "number": 5} +{"topic": "creating a mobile app", "number": 9} +{"topic": "maintaining a saltwater aquarium", "number": 7} +{"topic": "preparing for a baby's arrival", "number": 6} +{"topic": "writing a fantasy novel", "number": 5} +{"topic": "effective team leadership", "number": 8} +{"topic": "making a documentary film", "number": 9} +{"topic": "learning about historical events", "number": 7} +{"topic": "baking gluten-free treats", "number": 6} +{"topic": "improving mental arithmetic skills", "number": 5} +{"topic": "building a treehouse", "number": 8} +{"topic": "getting started with watercolor painting", "number": 9} +{"topic": "creating a YouTube tutorial series", "number": 7} +{"topic": "landscape photography", "number": 5} +{"topic": "navigating cultural differences", "number": 6} +{"topic": "preparing for a marathon", "number": 8} +{"topic": "building an online business", "number": 9} +{"topic": "learning to dance at home", "number": 5} +{"topic": "self-publishing a book", "number": 7} +{"topic": "starting an urban farm", "number": 6} +{"topic": "improving your memory", "number": 8} +{"topic": "creating a personal brand online", "number": 9} diff --git a/examples/teacher/teacher.pdl b/examples/teacher/teacher.pdl index 68dbe11db..ac1ba52ea 100644 --- a/examples/teacher/teacher.pdl +++ b/examples/teacher/teacher.pdl @@ -68,7 +68,7 @@ defs: input: ${teacher_input} parameters: temperature: 0 - stop_sequences: "${teacher_stop_token}" + stop: ["${teacher_stop_token}"] max_new_tokens: ${prompt_data.max_new_tokens} parser: regex: '### Question [0-9]+:\s*([^#\n]+)' @@ -156,7 +156,7 @@ defs: model: ${teacher_model} input: ${teacher_input} parameters: - stop_sequences: "${teacher_stop_token}" + stop: ["${teacher_stop_token}"] max_new_tokens: ${prompt_data.max_new_tokens} temperature: 0 parser: @@ -252,7 +252,7 @@ defs: model: ${teacher_model} input: ${teacher_input} parameters: - stop_sequences: ${ ([teacher_stop_token] + prompt_data.additional_stop_tokens) | join(',') } + stop: ["${ ([teacher_stop_token] + prompt_data.additional_stop_tokens) | join(',') }"] max_new_tokens: ${prompt_data.max_new_tokens} temperature: 0 parsed_answer: @@ -339,7 +339,7 @@ defs: model: ${teacher_model} input: ${teacher_input} parameters: - stop_sequences: "${teacher_stop_token}" + stop: ["${teacher_stop_token}"] max_new_tokens: ${prompt_data.max_new_tokens} temperature: 0 parser: diff --git a/pdl-live-react/package-lock.json b/pdl-live-react/package-lock.json index 3b97b3d0e..74144f1b7 100644 --- a/pdl-live-react/package-lock.json +++ b/pdl-live-react/package-lock.json @@ -1,12 +1,12 @@ { "name": "PDL", - "version": "0.6.0", + "version": "0.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "PDL", - "version": "0.6.0", + "version": "0.6.1", "dependencies": { "@patternfly/react-code-editor": "^6.1.0", "@patternfly/react-core": "^6.1.0", @@ -806,9 +806,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", "dev": true, "license": "MIT", "dependencies": { @@ -1269,9 +1269,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", - "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -1283,9 +1283,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", - "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -1297,9 +1297,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", - "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -1311,9 +1311,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", - "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -1325,9 +1325,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", - "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "cpu": [ "arm64" ], @@ -1339,9 +1339,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", - "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "cpu": [ "x64" ], @@ -1353,9 +1353,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", - "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -1367,9 +1367,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", - "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -1381,9 +1381,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", - "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -1395,9 +1395,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", - "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -1409,9 +1409,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", - "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", "cpu": [ "loong64" ], @@ -1423,9 +1423,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", - "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -1437,9 +1437,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", - "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", "cpu": [ "riscv64" ], @@ -1451,9 +1451,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", - "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -1465,9 +1465,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", - "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -1479,9 +1479,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", - "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -1493,9 +1493,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", - "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -1507,9 +1507,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", - "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -1521,9 +1521,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", - "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -1535,9 +1535,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", - "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -1549,9 +1549,9 @@ ] }, "node_modules/@tauri-apps/api": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.4.1.tgz", - "integrity": "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.5.0.tgz", + "integrity": "sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -1559,9 +1559,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.4.1.tgz", - "integrity": "sha512-9Ta81jx9+57FhtU/mPIckDcOBtPTUdKM75t4+aA0X84b8Sclb0jy1xA8NplmcRzp2fsfIHNngU2NiRxsW5+yOQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.5.0.tgz", + "integrity": "sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -1575,23 +1575,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.4.1", - "@tauri-apps/cli-darwin-x64": "2.4.1", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.4.1", - "@tauri-apps/cli-linux-arm64-gnu": "2.4.1", - "@tauri-apps/cli-linux-arm64-musl": "2.4.1", - "@tauri-apps/cli-linux-riscv64-gnu": "2.4.1", - "@tauri-apps/cli-linux-x64-gnu": "2.4.1", - "@tauri-apps/cli-linux-x64-musl": "2.4.1", - "@tauri-apps/cli-win32-arm64-msvc": "2.4.1", - "@tauri-apps/cli-win32-ia32-msvc": "2.4.1", - "@tauri-apps/cli-win32-x64-msvc": "2.4.1" + "@tauri-apps/cli-darwin-arm64": "2.5.0", + "@tauri-apps/cli-darwin-x64": "2.5.0", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", + "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", + "@tauri-apps/cli-linux-arm64-musl": "2.5.0", + "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-musl": "2.5.0", + "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", + "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", + "@tauri-apps/cli-win32-x64-msvc": "2.5.0" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.4.1.tgz", - "integrity": "sha512-QME7s8XQwy3LWClTVlIlwXVSLKkeJ/z88pr917Mtn9spYOjnBfsgHAgGdmpWD3NfJxjg7CtLbhH49DxoFL+hLg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==", "cpu": [ "arm64" ], @@ -1606,9 +1606,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.4.1.tgz", - "integrity": "sha512-/r89IcW6Ya1sEsFUEH7wLNruDTj7WmDWKGpPy7gATFtQr5JEY4heernqE82isjTUimnHZD8SCr0jA3NceI4ybw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz", + "integrity": "sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==", "cpu": [ "x64" ], @@ -1623,9 +1623,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.4.1.tgz", - "integrity": "sha512-9tDijkRB+CchAGjXxYdY9l/XzFpLp1yihUtGXJz9eh+3qIoRI043n3e+6xmU8ZURr7XPnu+R4sCmXs6HD+NCEQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.5.0.tgz", + "integrity": "sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==", "cpu": [ "arm" ], @@ -1640,9 +1640,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.4.1.tgz", - "integrity": "sha512-pnFGDEXBAzS4iDYAVxTRhAzNu3K2XPGflYyBc0czfHDBXopqRgMyj5Q9Wj7HAwv6cM8BqzXINxnb2ZJFGmbSgA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.5.0.tgz", + "integrity": "sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==", "cpu": [ "arm64" ], @@ -1657,9 +1657,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.1.tgz", - "integrity": "sha512-Hp0zXgeZNKmT+eoJSCxSBUm2QndNuRxR55tmIeNm3vbyUMJN/49uW7nurZ5fBPsacN4Pzwlx1dIMK+Gnr9A69w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==", "cpu": [ "arm64" ], @@ -1674,9 +1674,9 @@ } }, "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.4.1.tgz", - "integrity": "sha512-3T3bo2E4fdYRvzcXheWUeQOVB+LunEEi92iPRgOyuSVexVE4cmHYl+MPJF+EUV28Et0hIVTsHibmDO0/04lAFg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.5.0.tgz", + "integrity": "sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==", "cpu": [ "riscv64" ], @@ -1691,9 +1691,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.4.1.tgz", - "integrity": "sha512-kLN0FdNONO+2i+OpU9+mm6oTGufRC00e197TtwjpC0N6K2K8130w7Q3FeODIM2CMyg0ov3tH+QWqKW7GNhHFzg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.5.0.tgz", + "integrity": "sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==", "cpu": [ "x64" ], @@ -1708,9 +1708,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.1.tgz", - "integrity": "sha512-a8exvA5Ub9eg66a6hsMQKJIkf63QAf9OdiuFKOsEnKZkNN2x0NLgfvEcqdw88VY0UMs9dBoZ1AGbWMeYnLrLwQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==", "cpu": [ "x64" ], @@ -1725,9 +1725,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.4.1.tgz", - "integrity": "sha512-4JFrslsMCJQG1c573T9uqQSAbF3j/tMKkMWzsIssv8jvPiP++OG61A2/F+y9te9/Q/O95cKhDK63kaiO5xQaeg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.5.0.tgz", + "integrity": "sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==", "cpu": [ "arm64" ], @@ -1742,9 +1742,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.4.1.tgz", - "integrity": "sha512-9eXfFORehYSCRwxg2KodfmX/mhr50CI7wyBYGbPLePCjr5z0jK/9IyW6r0tC+ZVjwpX48dkk7hKiUgI25jHjzA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.5.0.tgz", + "integrity": "sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==", "cpu": [ "ia32" ], @@ -1759,9 +1759,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.4.1.tgz", - "integrity": "sha512-60a4Ov7Jrwqz2hzDltlS7301dhSAmM9dxo+IRBD3xz7yobKrgaHXYpWvnRomYItHcDd51VaKc9292H8/eE/gsw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.5.0.tgz", + "integrity": "sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==", "cpu": [ "x64" ], @@ -1977,17 +1977,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", - "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/type-utils": "8.29.1", - "@typescript-eslint/utils": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2007,16 +2007,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", - "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { @@ -2032,14 +2032,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", - "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2050,14 +2050,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", - "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -2074,9 +2074,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", - "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "license": "MIT", "engines": { @@ -2088,14 +2088,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", - "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2154,16 +2154,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", - "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2178,13 +2178,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", - "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/types": "8.30.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2202,17 +2202,17 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -2463,9 +2463,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001712", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", - "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", + "version": "1.0.30001714", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz", + "integrity": "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==", "dev": true, "funding": [ { @@ -2885,9 +2885,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -2924,9 +2924,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.134.tgz", - "integrity": "sha512-zSwzrLg3jNP3bwsLqWHmS5z2nIOQ5ngMnfMZOWWtXnqqQkPVyOipxK98w+1beLw1TB+EImPNcG8wVP/cLVs2Og==", + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", "dev": true, "license": "ISC" }, @@ -5296,9 +5296,9 @@ } }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", "engines": { @@ -5561,9 +5561,9 @@ } }, "node_modules/rollup": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz", - "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dev": true, "license": "MIT", "dependencies": { @@ -5577,26 +5577,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.39.0", - "@rollup/rollup-android-arm64": "4.39.0", - "@rollup/rollup-darwin-arm64": "4.39.0", - "@rollup/rollup-darwin-x64": "4.39.0", - "@rollup/rollup-freebsd-arm64": "4.39.0", - "@rollup/rollup-freebsd-x64": "4.39.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", - "@rollup/rollup-linux-arm-musleabihf": "4.39.0", - "@rollup/rollup-linux-arm64-gnu": "4.39.0", - "@rollup/rollup-linux-arm64-musl": "4.39.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-musl": "4.39.0", - "@rollup/rollup-linux-s390x-gnu": "4.39.0", - "@rollup/rollup-linux-x64-gnu": "4.39.0", - "@rollup/rollup-linux-x64-musl": "4.39.0", - "@rollup/rollup-win32-arm64-msvc": "4.39.0", - "@rollup/rollup-win32-ia32-msvc": "4.39.0", - "@rollup/rollup-win32-x64-msvc": "4.39.0", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, @@ -6010,15 +6010,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", - "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "@typescript-eslint/utils": "8.29.1" + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6206,15 +6206,18 @@ } }, "node_modules/vite": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", - "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.0.tgz", + "integrity": "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" }, "bin": { "vite": "bin/vite.js" diff --git a/pdl-live-react/package.json b/pdl-live-react/package.json index c9d3b6111..f5d186779 100644 --- a/pdl-live-react/package.json +++ b/pdl-live-react/package.json @@ -1,7 +1,7 @@ { "name": "PDL", "private": true, - "version": "0.6.0", + "version": "0.6.1", "type": "module", "scripts": { "prod:mac:1": "npm run tauri build -- --no-bundle --target=universal-apple-darwin", diff --git a/pdl-live-react/src-tauri/Cargo.lock b/pdl-live-react/src-tauri/Cargo.lock index 7b9b57c05..bc0570ac8 100644 --- a/pdl-live-react/src-tauri/Cargo.lock +++ b/pdl-live-react/src-tauri/Cargo.lock @@ -17,6 +17,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" version = "0.8.11" @@ -121,9 +127,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arraydeque" @@ -326,21 +332,19 @@ dependencies = [ ] [[package]] -name = "atomic-waker" -version = "1.1.2" +name = "atomic" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] [[package]] -name = "atty" -version = "0.2.14" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" @@ -396,6 +400,15 @@ dependencies = [ "serde", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -459,13 +472,13 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ - "lazy_static", "memchr", - "regex-automata 0.1.10", + "regex-automata", + "serde", ] [[package]] @@ -571,11 +584,20 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" -version = "1.2.18" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "shlex", ] @@ -642,18 +664,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -692,6 +714,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa 1.0.15", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -787,9 +823,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -862,6 +898,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", +] + [[package]] name = "ctor" version = "0.2.9" @@ -917,6 +962,37 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.100", +] + [[package]] name = "derive_more" version = "0.99.19" @@ -959,6 +1035,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1022,6 +1099,18 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "dns-lookup" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "windows-sys 0.48.0", +] + [[package]] name = "downcast-rs" version = "1.2.1" @@ -1257,6 +1346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", + "libz-rs-sys 0.5.0", "miniz_oxide", ] @@ -1323,6 +1413,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs4" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" +dependencies = [ + "rustix 1.0.5", + "windows-sys 0.59.0", +] + [[package]] name = "futf" version = "0.1.5" @@ -1553,13 +1653,23 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7131e57abbde63513e0e6636f76668a1ca9798dcae2df4e283cae9ee83859e" +dependencies = [ + "rustix 1.0.5", + "windows-targets 0.52.6", +] + [[package]] name = "getopts" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -1580,10 +1690,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1593,9 +1701,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -1754,9 +1864,13 @@ dependencies = [ [[package]] name = "half" -version = "1.8.3" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" @@ -1764,15 +1878,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.15.2" @@ -1803,15 +1908,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.9" @@ -2216,6 +2312,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -2315,6 +2420,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keyboard-types" version = "0.7.0" @@ -2340,10 +2454,14 @@ dependencies = [ ] [[package]] -name = "lalrpop-util" -version = "0.20.2" +name = "lambert_w" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +checksum = "91e80e7f3ce8e01aebf9062b5b4e1adfd928a0f4a903ffcf2d7787817615fb89" +dependencies = [ + "num-complex", + "num-traits", +] [[package]] name = "lazy_static" @@ -2353,9 +2471,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lexical-parse-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -2364,9 +2482,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "0.8.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" dependencies = [ "lexical-util", "static_assertions", @@ -2374,9 +2492,9 @@ dependencies = [ [[package]] name = "lexical-util" -version = "0.8.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" dependencies = [ "static_assertions", ] @@ -2401,15 +2519,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libffi" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c" +dependencies = [ + "cc", +] [[package]] name = "libloading" @@ -2421,6 +2558,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libm" version = "0.2.11" @@ -2437,6 +2584,24 @@ dependencies = [ "libc", ] +[[package]] +name = "libz-rs-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d" +dependencies = [ + "zlib-rs 0.4.2", +] + +[[package]] +name = "libz-rs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" +dependencies = [ + "zlib-rs 0.5.0", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2445,9 +2610,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -2487,36 +2652,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] -name = "malachite" -version = "0.4.22" +name = "mac_address" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbdf9cb251732db30a7200ebb6ae5d22fe8e11397364416617d2c2cf0c51cb5" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" dependencies = [ - "malachite-base", - "malachite-nz", - "malachite-q", + "nix 0.29.0", + "winapi", ] [[package]] name = "malachite-base" -version = "0.4.22" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea0ed76adf7defc1a92240b5c36d5368cfe9251640dcce5bd2d0b7c1fd87aeb" +checksum = "5063891d2cec8fd20cabccbd3fc277bf8d5666f481fb3f79d999559b39a62713" dependencies = [ - "hashbrown 0.14.5", - "itertools", + "hashbrown 0.15.2", + "itertools 0.11.0", "libm", "ryu", ] [[package]] name = "malachite-bigint" -version = "0.2.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d149aaa2965d70381709d9df4c7ee1fc0de1c614a4efc2ee356f5e43d68749f8" +checksum = "7b1b1fec8b370139968919a5b77071c94b282eaba3da1cf179ae5299060d4e75" dependencies = [ "derive_more 1.0.0", - "malachite", + "malachite-base", + "malachite-nz", "num-integer", "num-traits", "paste", @@ -2524,22 +2689,22 @@ dependencies = [ [[package]] name = "malachite-nz" -version = "0.4.22" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a79feebb2bc9aa7762047c8e5495269a367da6b5a90a99882a0aeeac1841f7" +checksum = "175263cd5b846c552b9afb9d4b03ca465b4ad10717d789cad7dac24441c4fcbe" dependencies = [ - "itertools", + "itertools 0.11.0", "libm", "malachite-base", ] [[package]] name = "malachite-q" -version = "0.4.22" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f235d5747b1256b47620f5640c2a17a88c7569eebdf27cd9cb130e1a619191" +checksum = "5261ba8feb1ad20cddab3d625af28206c663c08014b2e5c5f9bd54b0f230234f" dependencies = [ - "itertools", + "itertools 0.11.0", "malachite-base", "malachite-nz", ] @@ -2570,12 +2735,31 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -2603,9 +2787,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -2622,6 +2806,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mt19937" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7151a832e54d2d6b2c827a20e5bcdd80359281cd2c354e725d4b82e7c471de" +dependencies = [ + "rand_core 0.9.3", +] + [[package]] name = "muda" version = "0.16.1" @@ -2705,18 +2898,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "libc", - "memoffset", -] - [[package]] name = "nix" version = "0.28.0" @@ -3036,8 +3217,7 @@ dependencies = [ [[package]] name = "ollama-rs" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4b4750770584c8b4a643d0329e7bedacc4ecf68b7c7ac3e1fec2bafd6312f7" +source = "git+https://github.com/starpit/ollama-rs.git?branch=tools-pub-7#90820473b4b6e037a82f4c5bd2e254935bae8da7" dependencies = [ "async-stream", "log", @@ -3102,6 +3282,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-src" +version = "300.5.0+3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.107" @@ -3110,6 +3299,7 @@ checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -3152,6 +3342,16 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "pango" version = "0.18.3" @@ -3220,19 +3420,25 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pdl" -version = "0.6.0" +version = "0.6.1" dependencies = [ "async-recursion", "base64ct", + "derive_builder", "dirs", "duct", + "fs4", "futures", + "iana-time-zone", "indexmap 2.9.0", "minijinja", "ollama-rs", "owo-colors", "rayon", + "rustpython-pylib", + "rustpython-stdlib", "rustpython-vm", + "schemars", "serde", "serde_json", "serde_norway", @@ -3444,13 +3650,13 @@ dependencies = [ [[package]] name = "pmutil" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" +checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -3585,18 +3791,28 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] -name = "quick-xml" -version = "0.32.0" +name = "puruspe" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "d76c522e44709f541a403db419a7e34d6fbbc8e6b208589ae29a030cddeefd96" +dependencies = [ + "lambert_w", + "num-complex", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" dependencies = [ "memchr", ] @@ -3695,6 +3911,15 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3767,16 +3992,10 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", + "regex-automata", "regex-syntax", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - [[package]] name = "regex-automata" version = "0.4.9" @@ -3838,26 +4057,86 @@ dependencies = [ [[package]] name = "result-like" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810" +checksum = "abf7172fef6a7d056b5c26bf6c826570267562d51697f4982ff3ba4aec68a9df" dependencies = [ "result-like-derive", ] [[package]] name = "result-like-derive" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42" +checksum = "a8d6574c02e894d66370cfc681e5d68fedbc9a548fb55b30a96b3f0ae22d0fe5" dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 1.0.109", - "syn-ext", + "syn 2.0.100", +] + +[[package]] +name = "ruff_python_ast" +version = "0.0.0" +source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +dependencies = [ + "aho-corasick", + "bitflags 2.9.0", + "compact_str", + "is-macro", + "itertools 0.14.0", + "memchr", + "ruff_python_trivia", + "ruff_source_file", + "ruff_text_size", + "rustc-hash", +] + +[[package]] +name = "ruff_python_parser" +version = "0.0.0" +source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +dependencies = [ + "bitflags 2.9.0", + "bstr", + "compact_str", + "memchr", + "ruff_python_ast", + "ruff_python_trivia", + "ruff_text_size", + "rustc-hash", + "static_assertions", + "unicode-ident", + "unicode-normalization", + "unicode_names2", ] +[[package]] +name = "ruff_python_trivia" +version = "0.0.0" +source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +dependencies = [ + "itertools 0.14.0", + "ruff_source_file", + "ruff_text_size", + "unicode-ident", +] + +[[package]] +name = "ruff_source_file" +version = "0.0.0" +source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +dependencies = [ + "memchr", + "ruff_text_size", +] + +[[package]] +name = "ruff_text_size" +version = "0.0.0" +source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3866,9 +4145,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -3901,7 +4180,7 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -3920,116 +4199,122 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" -[[package]] -name = "rustpython-ast" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdaf8ee5c1473b993b398c174641d3aa9da847af36e8d5eb8291930b72f31a5" -dependencies = [ - "is-macro", - "malachite-bigint", - "rustpython-literal", - "rustpython-parser-core", - "static_assertions", -] - [[package]] name = "rustpython-codegen" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f101783403a69155ba7b52d8365d796c772a0bfca7df0a5f16d267f3443986" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ "ahash", "bitflags 2.9.0", "indexmap 2.9.0", - "itertools", + "itertools 0.14.0", "log", + "malachite-bigint", + "memchr", "num-complex", "num-traits", - "rustpython-ast", + "ruff_python_ast", + "ruff_source_file", + "ruff_text_size", "rustpython-compiler-core", - "rustpython-parser-core", + "rustpython-compiler-source", + "rustpython-literal", + "rustpython-wtf8", + "thiserror 2.0.12", + "unicode_names2", ] [[package]] name = "rustpython-common" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22a5c520662f0ff98d717e2c4e52d8ba35eb1d99ee771dbdba7f09908b75bbb" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ "ascii", "bitflags 2.9.0", "bstr", "cfg-if", - "itertools", + "getrandom 0.3.2", + "itertools 0.14.0", "libc", "lock_api", "malachite-base", "malachite-bigint", "malachite-q", - "num-complex", + "memchr", "num-traits", "once_cell", + "parking_lot", "radium", - "rand 0.8.5", - "rustpython-format", - "siphasher 0.3.11", + "rustpython-literal", + "rustpython-wtf8", + "siphasher 1.0.1", + "unicode_names2", "volatile", "widestring", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustpython-compiler" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c0ad9d5b948970d41b113cfc9ffe2337b10fdd41e0c92a859b9ed33c218efe" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ + "ruff_python_ast", + "ruff_python_parser", + "ruff_source_file", + "ruff_text_size", "rustpython-codegen", "rustpython-compiler-core", - "rustpython-parser", + "rustpython-compiler-source", + "thiserror 2.0.12", ] [[package]] name = "rustpython-compiler-core" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd4e0c9fb7b3c70eb27b38d533edc0aa4875ea38cb06e12d76e234d00ef9766" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ "bitflags 2.9.0", - "itertools", + "itertools 0.14.0", "lz4_flex", "malachite-bigint", "num-complex", - "rustpython-parser-core", + "ruff_source_file", + "rustpython-wtf8", +] + +[[package]] +name = "rustpython-compiler-source" +version = "0.4.0" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" +dependencies = [ + "ruff_source_file", + "ruff_text_size", ] [[package]] name = "rustpython-derive" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c39620497116ce2996bcc679f9be4f47c1e8915c7ff9a9f0324e9584280660" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ + "proc-macro2", "rustpython-compiler", "rustpython-derive-impl", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] name = "rustpython-derive-impl" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd18fa95c71a08ecc9cce739a608f5bff38805963152e671b66f5f266f0e58d" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ - "itertools", + "itertools 0.14.0", "maplit", - "once_cell", "proc-macro2", "quote", "rustpython-compiler-core", "rustpython-doc", - "rustpython-parser-core", - "syn 1.0.109", + "syn 2.0.100", "syn-ext", "textwrap", ] @@ -4037,124 +4322,147 @@ dependencies = [ [[package]] name = "rustpython-doc" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885d19895d9d29656a8a2b33e967a482b92f3d891b4fd923e40849714051bcd" +source = "git+https://github.com/RustPython/__doc__?tag=0.3.0#8b62ce5d796d68a091969c9fa5406276cb483f79" dependencies = [ "once_cell", ] -[[package]] -name = "rustpython-format" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0389039b132ad8e350552d771270ccd03186985696764bcee2239694e7839942" -dependencies = [ - "bitflags 2.9.0", - "itertools", - "malachite-bigint", - "num-traits", - "rustpython-literal", -] - [[package]] name = "rustpython-literal" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8304be3cae00232a1721a911033e55877ca3810215f66798e964a2d8d22281d" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ "hexf-parse", "is-macro", "lexical-parse-float", "num-traits", + "rustpython-wtf8", "unic-ucd-category", ] [[package]] -name = "rustpython-parser" +name = "rustpython-pylib" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "868f724daac0caf9bd36d38caf45819905193a901e8f1c983345a68e18fb2abb" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ - "anyhow", - "is-macro", - "itertools", - "lalrpop-util", - "log", - "malachite-bigint", - "num-traits", - "phf 0.11.3", - "phf_codegen 0.11.3", - "rustc-hash", - "rustpython-ast", - "rustpython-parser-core", - "tiny-keccak", - "unic-emoji-char", - "unic-ucd-ident", - "unicode_names2", + "glob", + "rustpython-compiler-core", + "rustpython-derive", ] [[package]] -name = "rustpython-parser-core" +name = "rustpython-sre_engine" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b6c12fa273825edc7bccd9a734f0ad5ba4b8a2f4da5ff7efe946f066d0f4ad" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ - "is-macro", - "memchr", - "rustpython-parser-vendored", + "bitflags 2.9.0", + "num_enum", + "optional", + "rustpython-wtf8", ] [[package]] -name = "rustpython-parser-vendored" +name = "rustpython-stdlib" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04fcea49a4630a3a5d940f4d514dc4f575ed63c14c3e3ed07146634aed7f67a6" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ + "adler32", + "ahash", + "ascii", + "base64 0.22.1", + "blake2", + "cfg-if", + "crc32fast", + "crossbeam-utils", + "csv-core", + "digest", + "dns-lookup", + "dyn-clone", + "flate2", + "foreign-types-shared 0.1.1", + "gethostname", + "hex", + "indexmap 2.9.0", + "itertools 0.14.0", + "junction", + "libc", + "libz-rs-sys 0.4.2", + "mac_address", + "malachite-bigint", + "md-5", "memchr", - "once_cell", -] - -[[package]] -name = "rustpython-sre_engine" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39367be5d48e1e5caaa146904ea8d35fe43928168fbeb5c1ab295a0031b179c6" -dependencies = [ - "bitflags 2.9.0", + "memmap2", + "mt19937", + "nix 0.29.0", + "num-complex", + "num-integer", + "num-traits", "num_enum", - "optional", + "openssl", + "openssl-probe", + "openssl-sys", + "page_size", + "parking_lot", + "paste", + "puruspe", + "rand_core 0.9.3", + "rustix 0.38.44", + "rustpython-common", + "rustpython-derive", + "rustpython-vm", + "schannel", + "sha-1", + "sha2", + "sha3", + "socket2", + "system-configuration", + "termios", + "ucd", + "unic-char-property", + "unic-normal", + "unic-ucd-age", + "unic-ucd-bidi", + "unic-ucd-category", + "unic-ucd-ident", + "unicode-casing", + "unicode_names2", + "uuid", + "widestring", + "windows-sys 0.59.0", + "xml-rs", ] [[package]] name = "rustpython-vm" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2878cc4b5679f35fa762891d812ca7e011ae7cd41b5c532eb0ad13959b522493" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" dependencies = [ "ahash", "ascii", - "atty", "bitflags 2.9.0", "bstr", "caseless", "cfg-if", "chrono", "crossbeam-utils", + "errno", "exitcode", - "getrandom 0.2.15", + "getrandom 0.3.2", "glob", "half", "hex", "indexmap 2.9.0", "is-macro", - "itertools", + "itertools 0.14.0", "junction", "libc", + "libffi", + "libloading 0.8.6", "log", "malachite-bigint", "memchr", "memoffset", - "nix 0.27.1", + "nix 0.29.0", "num-complex", "num-integer", "num-traits", @@ -4164,26 +4472,26 @@ dependencies = [ "optional", "parking_lot", "paste", - "rand 0.8.5", "result-like", - "rustc_version", - "rustpython-ast", + "ruff_python_ast", + "ruff_python_parser", + "ruff_source_file", + "ruff_text_size", + "rustix 0.38.44", "rustpython-codegen", "rustpython-common", "rustpython-compiler", "rustpython-compiler-core", + "rustpython-compiler-source", "rustpython-derive", - "rustpython-format", "rustpython-literal", - "rustpython-parser", - "rustpython-parser-core", "rustpython-sre_engine", "rustyline", "schannel", "static_assertions", "strum", "strum_macros", - "thiserror 1.0.69", + "thiserror 2.0.12", "thread_local", "timsort", "uname", @@ -4196,8 +4504,19 @@ dependencies = [ "which", "widestring", "windows 0.52.0", - "windows-sys 0.52.0", - "winreg 0.10.1", + "windows-sys 0.59.0", + "winreg 0.55.0", +] + +[[package]] +name = "rustpython-wtf8" +version = "0.4.0" +source = "git+https://github.com/RustPython/RustPython.git#a917da3b1a0b025f4d82708eb11e802377128dd0" +dependencies = [ + "ascii", + "bstr", + "itertools 0.14.0", + "memchr", ] [[package]] @@ -4208,9 +4527,9 @@ checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rustyline" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" +checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" dependencies = [ "bitflags 2.9.0", "cfg-if", @@ -4220,12 +4539,12 @@ dependencies = [ "libc", "log", "memchr", - "nix 0.28.0", + "nix 0.29.0", "radix_trie", "unicode-segmentation", - "unicode-width", + "unicode-width 0.2.0", "utf8parse", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4468,9 +4787,9 @@ dependencies = [ [[package]] name = "serial2" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd0c773455b60177d1abe4c739cbfa316c4f2f0ef37465befcb72e8a15cdd02" +checksum = "c7d1d08630509d69f90eff4afcd02c3bd974d979225cbd815ff5942351b14375" dependencies = [ "cfg-if", "libc", @@ -4509,6 +4828,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -4520,6 +4850,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shared_child" version = "1.0.1" @@ -4697,23 +5037,29 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.24.1" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" [[package]] name = "strum_macros" -version = "0.24.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 1.0.109", + "syn 2.0.100", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swift-rs" version = "1.0.7" @@ -4749,11 +5095,13 @@ dependencies = [ [[package]] name = "syn-ext" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6" +checksum = "b126de4ef6c2a628a68609dd00733766c3b015894698a438ebdf374933fc31d1" dependencies = [ - "syn 1.0.109", + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -4776,6 +5124,27 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -4791,9 +5160,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.32.8" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" +checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" dependencies = [ "bitflags 2.9.0", "core-foundation 0.10.0", @@ -4822,8 +5191,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.1", + "windows-core 0.61.0", "windows-version", "x11-dl", ] @@ -4847,9 +5216,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a" +checksum = "be03adf68fba02f87c4653da7bd73f40b0ecf9c6b7c2c39830f6981d0651912f" dependencies = [ "anyhow", "bytes", @@ -4870,6 +5239,7 @@ dependencies = [ "objc2 0.6.0", "objc2-app-kit", "objc2-foundation 0.3.0", + "objc2-ui-kit", "percent-encoding", "plist", "raw-window-handle", @@ -4892,14 +5262,14 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.60.0", + "windows 0.61.1", ] [[package]] name = "tauri-build" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17" +checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" dependencies = [ "anyhow", "cargo_toml", @@ -4919,9 +5289,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" +checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" dependencies = [ "base64 0.22.1", "brotli", @@ -4946,9 +5316,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f" +checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4960,9 +5330,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d" +checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601" dependencies = [ "anyhow", "glob", @@ -5042,29 +5412,31 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532" +checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" dependencies = [ "cookie", "dpi", "gtk", "http", "jni", + "objc2 0.6.0", + "objc2-ui-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror 2.0.12", "url", - "windows 0.60.0", + "windows 0.61.1", ] [[package]] name = "tauri-runtime-wry" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421" +checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" dependencies = [ "gtk", "http", @@ -5083,15 +5455,15 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows 0.60.0", + "windows 0.61.1", "wry", ] [[package]] name = "tauri-utils" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" +checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" dependencies = [ "anyhow", "brotli", @@ -5159,11 +5531,20 @@ dependencies = [ "utf-8", ] +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + [[package]] name = "textwrap" -version = "0.15.2" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" [[package]] name = "thin-slice" @@ -5258,15 +5639,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "639ce8ef6d2ba56be0383a94dd13b92138d58de44c62618303bb798fa92bdc00" -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -5519,6 +5891,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "ucd" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4fa6e588762366f1eb4991ce59ad1b93651d0b769dfb4e4d1c5c4b943d1159" + [[package]] name = "uds_windows" version = "1.1.0" @@ -5561,10 +5939,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" [[package]] -name = "unic-emoji-char" +name = "unic-normal" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +checksum = "f09d64d33589a94628bc2aeb037f35c2e25f3f049c7348b5aa5580b48e6bba62" +dependencies = [ + "unic-ucd-normal", +] + +[[package]] +name = "unic-ucd-age" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8cfdfe71af46b871dc6af2c24fcd360e2f3392ee4c5111877f2947f311671c" dependencies = [ "unic-char-property", "unic-char-range", @@ -5594,6 +5981,15 @@ dependencies = [ "unic-ucd-version", ] +[[package]] +name = "unic-ucd-hangul" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1dc690e19010e1523edb9713224cba5ef55b54894fe33424439ec9a40c0054" +dependencies = [ + "unic-ucd-version", +] + [[package]] name = "unic-ucd-ident" version = "0.9.0" @@ -5605,6 +6001,18 @@ dependencies = [ "unic-ucd-version", ] +[[package]] +name = "unic-ucd-normal" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86aed873b8202d22b13859dda5fe7c001d271412c31d411fd9b827e030569410" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-hangul", + "unic-ucd-version", +] + [[package]] name = "unic-ucd-version" version = "0.9.0" @@ -5647,6 +6055,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5741,6 +6155,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ + "atomic", "getrandom 0.3.2", "serde", ] @@ -5969,15 +6384,15 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" +checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.60.0", - "windows-core 0.60.1", - "windows-implement 0.59.0", + "windows 0.61.1", + "windows-core 0.61.0", + "windows-implement 0.60.0", "windows-interface", ] @@ -5994,25 +6409,25 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" +checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" dependencies = [ "thiserror 2.0.12", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.1", + "windows-core 0.61.0", ] [[package]] name = "which" -version = "4.4.2" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", - "once_cell", "rustix 0.38.44", + "winsafe", ] [[package]] @@ -6083,11 +6498,24 @@ version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" dependencies = [ - "windows-collections", + "windows-collections 0.1.1", "windows-core 0.60.1", - "windows-future", + "windows-future 0.1.1", + "windows-link", + "windows-numerics 0.1.1", +] + +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.0", + "windows-future 0.2.0", "windows-link", - "windows-numerics", + "windows-numerics 0.2.0", ] [[package]] @@ -6099,6 +6527,15 @@ dependencies = [ "windows-core 0.60.1", ] +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.0", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -6144,6 +6581,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-implement" version = "0.59.0" @@ -6193,6 +6640,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-registry" version = "0.4.0" @@ -6555,6 +7012,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -6578,9 +7051,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.50.5" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" +checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" dependencies = [ "base64 0.22.1", "block2 0.6.0", @@ -6614,8 +7087,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.1", + "windows-core 0.61.0", "windows-version", "x11-dl", ] @@ -6651,6 +7124,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "xml-rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" + [[package]] name = "yaml-rust2" version = "0.10.1" @@ -6832,6 +7311,18 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "zlib-rs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05" + +[[package]] +name = "zlib-rs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" + [[package]] name = "zvariant" version = "5.4.0" diff --git a/pdl-live-react/src-tauri/Cargo.toml b/pdl-live-react/src-tauri/Cargo.toml index 40155af98..ae0f93ba4 100644 --- a/pdl-live-react/src-tauri/Cargo.toml +++ b/pdl-live-react/src-tauri/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "pdl" -version = "0.6.0" +version = "0.6.1" description = "Prompt Declaration Language" authors = ["nickm@us.ibm.com"] -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -35,13 +35,20 @@ base64ct = { version = "1.7.1", features = ["alloc"] } dirs = "6.0.0" serde_norway = "0.9.42" minijinja = { version = "2.9.0", features = ["custom_syntax"] } -ollama-rs = { version = "0.3.0", features = ["stream"] } +#ollama-rs = { version = "0.3.0", features = ["stream"] } +ollama-rs = { git = "https://github.com/starpit/ollama-rs.git", branch = "tools-pub-7", features = ["stream"] } owo-colors = "4.2.0" -rustpython-vm = "0.4.0" +rustpython-vm = { git="https://github.com/RustPython/RustPython.git", features= ["importlib", "threading", "encodings"] } # "0.4.0" async-recursion = "1.1.1" tokio-stream = "0.1.17" tokio = { version = "1.44.1", features = ["io-std"] } indexmap = { version = "2.9.0", features = ["serde"] } +rustpython-stdlib = { git="https://github.com/RustPython/RustPython.git", features = ["ssl-vendor"] } # 0.4.0 +rustpython-pylib = { git="https://github.com/RustPython/RustPython.git", features = ["freeze-stdlib"] } # 0.4.0 +schemars = "0.8.22" +fs4 = "0.13.1" +derive_builder = "0.20.2" +iana-time-zone = "0.1.63" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-cli = "2" diff --git a/pdl-live-react/src-tauri/src/cli.rs b/pdl-live-react/src-tauri/src/cli.rs index 06e15541b..a85a6b6fd 100644 --- a/pdl-live-react/src-tauri/src/cli.rs +++ b/pdl-live-react/src-tauri/src/cli.rs @@ -5,8 +5,7 @@ use urlencoding::encode; use crate::compile; use crate::gui::new_window; -use crate::pdl::interpreter::run_file_sync as runr; -use crate::pdl::run::run_pdl_program; +use crate::pdl::interpreter::{RunOptions, load_scope, run_file_sync}; #[cfg(desktop)] pub fn setup(app: &mut tauri::App) -> Result> { @@ -34,7 +33,7 @@ pub fn setup(app: &mut tauri::App) -> Result> let args = compile_subcommand_matches.matches.args; match compile_subcommand_matches.name.as_str() { - "beeai" => compile::beeai::compile( + "beeai" => compile::beeai::compile_to_file( args.get("source") .and_then(|a| a.value.as_str()) .expect("valid positional source arg"), @@ -50,28 +49,33 @@ pub fn setup(app: &mut tauri::App) -> Result> _ => Err(Box::from("Unsupported compile command")), } } - "runr" => runr( + "run" => run_file_sync( subcommand_args .get("source") .and_then(|a| a.value.as_str()) .expect("valid positional source arg"), - subcommand_args - .get("debug") - .and_then(|a| a.value.as_bool()) - .or(Some(false)) - == Some(true), + RunOptions { + trace: subcommand_args.get("trace").and_then(|a| a.value.as_str()), + debug: subcommand_args + .get("debug") + .and_then(|a| a.value.as_bool()) + .or(Some(false)) + == Some(true), + stream: subcommand_args + .get("no-stream") + .and_then(|a| a.value.as_bool()) + .or(Some(false)) + == Some(false), + }, + load_scope( + subcommand_args.get("data").and_then(|a| a.value.as_str()), + subcommand_args + .get("data-file") + .and_then(|a| a.value.as_str()), + None, + )?, ) .and_then(|_trace| Ok(true)), - "run" => run_pdl_program( - subcommand_args - .get("source") - .and_then(|a| a.value.as_str()) - .expect("valid positional source arg"), - subcommand_args.get("trace").and_then(|a| a.value.as_str()), - subcommand_args.get("data").and_then(|a| a.value.as_str()), - subcommand_args.get("stream").and_then(|a| a.value.as_str()), - ) - .and_then(|()| Ok(true)), "view" => new_window( app.handle().clone(), subcommand_args.get("trace").and_then(|a| { diff --git a/pdl-live-react/src-tauri/src/commands/interpreter.rs b/pdl-live-react/src-tauri/src/commands/interpreter.rs index ff59f47e2..46bf804a9 100644 --- a/pdl-live-react/src-tauri/src/commands/interpreter.rs +++ b/pdl-live-react/src-tauri/src/commands/interpreter.rs @@ -1,8 +1,8 @@ use crate::pdl::interpreter::{pretty_print, run_string}; #[tauri::command] -pub async fn run_pdl_program(program: String, debug: bool) -> Result { - let (_, messages, _) = run_string(&program, debug) +pub async fn run_pdl_program(program: String) -> Result { + let (_, messages, _) = run_string(&program, Default::default(), Default::default()) .await .map_err(|err| err.to_string())?; diff --git a/pdl-live-react/src-tauri/src/compile/beeai.rs b/pdl-live-react/src-tauri/src/compile/beeai.rs index c686f2119..27c1e9fc4 100644 --- a/pdl-live-react/src-tauri/src/compile/beeai.rs +++ b/pdl-live-react/src-tauri/src/compile/beeai.rs @@ -8,13 +8,14 @@ use ::std::path::{Path, PathBuf}; use duct::cmd; use futures::executor::block_on; use serde::Deserialize; -use serde_json::{from_reader, json, to_string, Map, Value}; +use serde_json::{Map, Value, from_reader, json, to_string}; use tempfile::Builder; use crate::pdl::ast::{ - ArrayBlock, CallBlock, FunctionBlock, ListOrString, MessageBlock, ModelBlock, ObjectBlock, - PdlBaseType, PdlBlock, PdlOptionalType, PdlParser, PdlType, PythonCodeBlock, RepeatBlock, Role, - TextBlock, + ArrayBlockBuilder, Block, Body::*, CallBlock, EvalsTo, Expr, FunctionBlock, ListOrString, + MessageBlock, MetadataBuilder, ModelBlockBuilder, ObjectBlock, PdlBaseType, PdlBlock, + PdlBlock::Advanced, PdlOptionalType, PdlParser, PdlType, PythonCodeBlock, RepeatBlock, Role, + TextBlock, TextBlockBuilder, }; use crate::pdl::pip::pip_install_if_needed; use crate::pdl::requirements::BEEAI_FRAMEWORK; @@ -190,46 +191,86 @@ fn with_tools( } fn call_tools(model: &String, parameters: &HashMap) -> PdlBlock { - let repeat = PdlBlock::Text(TextBlock { - def: None, - defs: None, - role: None, - parser: None, - description: Some("Calling tool ${ tool.function.name }".to_string()), - text: vec![PdlBlock::Model( - ModelBlock::new(model.as_str()) - .parameters(&strip_nulls(parameters)) - .input(PdlBlock::Array(ArrayBlock { - array: vec![PdlBlock::Message(MessageBlock { - role: Role::Tool, - description: None, - name: Some("${ tool.function.name }".to_string()), - tool_call_id: Some("${ tool.id }".to_string()), - content: Box::new(PdlBlock::Call(CallBlock { - defs: json_loads( - &"args", - &"pdl__args", - &"${ tool.function.arguments }", + let repeat = Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .description("Calling tool ${ tool.function.name }".to_string()) + .build() + .unwrap(), + ), + + body: Text(TextBlock { + role: None, + parser: None, + text: vec![Advanced(Block { + metadata: None, + body: Model( + ModelBlockBuilder::default() + .model(model.as_str()) + .parameters(strip_nulls(parameters)) + .input(Advanced(Block { + metadata: None, + body: Array( + ArrayBlockBuilder::default() + .array(vec![Advanced(Block { + metadata: None, + body: Message(MessageBlock { + role: Role::Tool, + defsite: None, + name: Some("${ tool.function.name }".to_string()), + tool_call_id: Some("${ tool.id }".to_string()), + content: Box::new(Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .defs(json_loads( + &"args", + &"pdl__args", + &"${ tool.function.arguments }", + )) + .build() + .unwrap(), + ), + + body: Call(CallBlock { + call: EvalsTo::Jinja( + "${ pdl__tools[tool.function.name] }" + .to_string(), + ), // look up tool in tool_declarations def (see below) + args: Some("${ args }".into()), // invoke with arguments as specified by the model + pdl_trace: None, + }), + })), + }), + })]) + .build() + .unwrap(), ), - call: "${ pdl__tools[tool.function.name] }".to_string(), // look up tool in tool_declarations def (see below) - args: Some("${ args }".into()), // invoke with arguments as specified by the model - })), - })], - })) - .build(), - )], + })) + .build() + .unwrap(), + ), + })], + }), }); let mut for_ = HashMap::new(); for_.insert( "tool".to_string(), - ListOrString::String("${ response.choices[0].message.tool_calls }".to_string()), + EvalsTo::Expr(Expr { + pdl_expr: ListOrString::String( + "${ response.choices[0].message.tool_calls }".to_string(), + ), + pdl_result: None, + }), ); // response.choices[0].message.tool_calls - PdlBlock::Repeat(RepeatBlock { - for_: for_, - repeat: Box::new(repeat), + Advanced(Block { + metadata: None, + body: Repeat(RepeatBlock { + for_: for_, + repeat: Box::new(repeat), + }), }) } @@ -237,21 +278,31 @@ fn json_loads( outer_name: &str, inner_name: &str, value: &str, -) -> Option> { +) -> indexmap::IndexMap { let mut m = indexmap::IndexMap::new(); m.insert( outer_name.to_owned(), - PdlBlock::Text( - TextBlock::new(vec![PdlBlock::String(format!( - "{{\"{}\": {}}}", - inner_name, value - ))]) - .description(format!("Parsing json for {}={}", inner_name, value)) - .parser(PdlParser::Json) - .build(), - ), + Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .description(format!("Parsing json for {}={}", inner_name, value)) + .build() + .unwrap(), + ), + + body: Text( + TextBlockBuilder::default() + .text(vec![PdlBlock::String(format!( + "{{\"{}\": {}}}", + inner_name, value + ))]) + .parser(PdlParser::Json) + .build() + .unwrap(), + ), + }), ); - Some(m) + m } fn json_schema_type_to_pdl_type(spec: &Value) -> PdlType { @@ -348,15 +399,7 @@ fn python_source_to_json(source_file_path: &str, debug: bool) -> Result Result<(), Box> { - if debug { - eprintln!("Compiling beeai {} to {}", source_file_path, output_path); - } - +pub fn compile(source_file_path: &str, debug: bool) -> Result> { let file = match Path::new(source_file_path) .extension() .and_then(OsStr::to_str) @@ -406,11 +449,13 @@ pub fn compile( tool_name.clone(), PdlBlock::Function(FunctionBlock { function: schema, - return_: Box::new(PdlBlock::PythonCode(PythonCodeBlock { - // tool function definition - lang: "python".to_string(), - code: format!( - " + return_: Box::new(Advanced(Block { + metadata: None, + body: PythonCode(PythonCodeBlock { + // tool function definition + lang: "python".to_string(), + code: format!( + " from {} import {} import asyncio async def invoke(): @@ -422,23 +467,24 @@ async def invoke(): {} asyncio.run(invoke()) ", - import_from, - import_fn, - if debug { - format!("print('Invoking tool {}')", tool_name) - } else { - "".to_string() - }, - import_fn, - if debug { - format!( - "print(f'Response from tool {}: {{result}}')", - tool_name - ) - } else { - "".to_string() - } - ), + import_from, + import_fn, + if debug { + format!("print('Invoking tool {}')", tool_name) + } else { + "".to_string() + }, + import_fn, + if debug { + format!( + "print(f'Response from tool {}: {{result}}')", + tool_name + ) + } else { + "".to_string() + } + ), + }), })), }), ) @@ -468,34 +514,43 @@ asyncio.run(invoke()) let model = format!("{}/{}", provider, model); if let Some(instructions) = instructions { - model_call.push(PdlBlock::Text(TextBlock { - role: Some(Role::System), - text: vec![PdlBlock::String(instructions)], - def: None, - defs: None, - parser: None, - description: Some("Model instructions".into()), + model_call.push(Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .description("Model instructions".to_string()) + .build() + .unwrap(), + ), + body: Text(TextBlock { + role: Some(Role::System), + text: vec![PdlBlock::String(instructions)], + parser: None, + }), })); } - let model_response = if let Some(tools) = &tools { - match tools.len() { - 0 => None, - _ => Some("response".to_string()), + let mut model_builder = ModelBlockBuilder::default(); + model_builder + .model(model.clone()) + .parameters(with_tools(&tools, ¶meters.state.dict)); + + if let Some(tools) = &tools { + if tools.len() > 0 { + // then we want the model response as a + // "response" variable, so we can scan for + // tool calls + model_builder.model_response("response".to_string()); } - } else { - None - }; - - model_call.push(PdlBlock::Model(ModelBlock { - input: None, - description: Some(description), - def: None, - model: model.clone(), - model_response: model_response, - pdl_result: None, - pdl_usage: None, - parameters: Some(with_tools(&tools, ¶meters.state.dict)), + } + + model_call.push(Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .description(description) + .build() + .unwrap(), + ), + body: Model(model_builder.build().unwrap()), })); if let Some(tools) = tools { @@ -510,26 +565,37 @@ asyncio.run(invoke()) closure_name.clone(), PdlBlock::Function(FunctionBlock { function: HashMap::new(), - return_: Box::new(PdlBlock::Text(TextBlock { - def: None, - defs: None, - role: None, - parser: None, - description: Some(format!("Model call {}", &model)), - text: model_call, + return_: Box::new(Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .description(format!("Model call {}", &model)) + .build() + .unwrap(), + ), + body: Text(TextBlock { + role: None, + parser: None, + text: model_call, + }), })), }), ); - PdlBlock::Text(TextBlock { - def: None, - defs: Some(defs), - role: None, - parser: None, - description: Some("Model call wrapper".to_string()), - text: vec![PdlBlock::Call(CallBlock::new(format!( - "${{ {} }}", - closure_name - )))], + Advanced(Block { + metadata: Some( + MetadataBuilder::default() + .description("Model call wrapper".to_string()) + .defs(defs) + .build() + .unwrap(), + ), + body: Text(TextBlock { + role: None, + parser: None, + text: vec![Advanced(Block { + metadata: None, + body: Call(CallBlock::new(format!("${{ {} }}", closure_name))), + })], + }), }) }, ) @@ -539,26 +605,45 @@ asyncio.run(invoke()) .flat_map(|(a, b)| [a, b]) .collect::>(); - let pdl: PdlBlock = PdlBlock::Text(TextBlock { - def: None, - defs: if tool_declarations.len() == 0 { - None - } else { - let mut m = indexmap::IndexMap::new(); - m.insert( - "pdl__tools".to_string(), - PdlBlock::Object(ObjectBlock { + let mut metadata = MetadataBuilder::default(); + metadata.description(bee.workflow.workflow.name); + if tool_declarations.len() > 0 { + let mut defs = indexmap::IndexMap::new(); + defs.insert( + "pdl__tools".to_string(), + Advanced(Block { + metadata: None, + body: Object(ObjectBlock { object: tool_declarations, }), - ); - Some(m) - }, - description: Some(bee.workflow.workflow.name), - role: None, - parser: None, - text: body, + }), + ); + metadata.defs(defs); + } + + let pdl: PdlBlock = Advanced(Block { + metadata: Some(metadata.build().unwrap()), + body: Text(TextBlock { + role: None, + parser: None, + text: body, + }), }); + Ok(pdl) +} + +pub fn compile_to_file( + source_file_path: &str, + output_path: &str, + debug: bool, +) -> Result<(), Box> { + if debug { + eprintln!("Compiling beeai {} to {}", source_file_path, output_path); + } + + let pdl = compile(source_file_path, debug)?; + match output_path { "-" => println!("{}", to_string(&pdl)?), _ => { diff --git a/pdl-live-react/src-tauri/src/gui.rs b/pdl-live-react/src-tauri/src/gui.rs index 8b2515e42..60672534a 100644 --- a/pdl-live-react/src-tauri/src/gui.rs +++ b/pdl-live-react/src-tauri/src/gui.rs @@ -11,6 +11,7 @@ pub fn new_window( tauri::WebviewUrl::App(path.unwrap_or("".into())), ) .title("Prompt Declaration Language") + .prevent_overflow() .zoom_hotkeys_enabled(true) .inner_size(1400.0, 1050.0) .build()?; diff --git a/pdl-live-react/src-tauri/src/pdl/ast.rs b/pdl-live-react/src-tauri/src/pdl/ast.rs index 5fd8d24ad..67dbf7f19 100644 --- a/pdl-live-react/src-tauri/src/pdl/ast.rs +++ b/pdl-live-react/src-tauri/src/pdl/ast.rs @@ -1,7 +1,10 @@ use ::std::collections::HashMap; +use ::std::error::Error; +use ::std::time::SystemTime; + use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use serde_json::{to_string, Number, Value}; +use serde_json::{Number, Value, to_string}; #[derive(Serialize, Deserialize, Debug, Clone)] //why doesn't this work? #[serde(rename_all_fields(serialize = "lowercase"))] @@ -51,82 +54,119 @@ pub enum PdlType { Object(HashMap), } +/// Timing information +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct Timing { + // TODO serde_json doesn't support u128, but (below) as_nanos() returns u128... + start_nanos: u64, + end_nanos: u64, + timezone: String, +} + +type TimingError = Box; +impl Timing { + fn now() -> Result { + Ok(::std::time::SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH)? + .as_nanos() as u64) + } + + pub fn start() -> Result { + let mut t = Timing::default(); + t.start_nanos = Timing::now()?; + t.timezone = iana_time_zone::get_timezone()?; + Ok(t) + } + + pub fn end(&mut self) -> Result<(), TimingError> { + self.end_nanos = Timing::now()?; + Ok(()) + } +} + +/// Common metadata of blocks +#[derive(Serialize, Deserialize, Debug, Clone, Default, derive_builder::Builder)] +#[serde(default)] +#[builder(setter(into, strip_option), default)] +pub struct Metadata { + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub defs: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub def: Option, + + #[serde(rename = "pdl__id", skip_serializing_if = "Option::is_none")] + pub pdl_id: Option, + + #[serde(rename = "pdl__result", skip_serializing_if = "Option::is_none")] + pub pdl_result: Option>, + + #[serde(rename = "pdl__is_leaf", skip_serializing_if = "Option::is_none")] + pub pdl_is_leaf: Option, + + #[serde(rename = "pdl__timing", skip_serializing_if = "Option::is_none")] + pub pdl_timing: Option, +} + /// Call a function #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "call")] pub struct CallBlock { - /// Function to call - pub call: String, + pub call: EvalsTo>, /// Arguments of the function with their values #[serde(skip_serializing_if = "Option::is_none")] pub args: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub defs: Option>, + #[serde(rename = "pdl__trace", skip_serializing_if = "Option::is_none")] + pub pdl_trace: Option>, } impl CallBlock { pub fn new(call: String) -> Self { CallBlock { - call: call, + call: EvalsTo::Jinja(call), args: None, - defs: None, + pdl_trace: None, } } } pub trait SequencingBlock { fn kind(&self) -> &str; - fn description(&self) -> &Option; fn role(&self) -> &Option; - fn def(&self) -> &Option; - fn defs(&self) -> &Option>; fn items(&self) -> &Vec; fn with_items(&self, items: Vec) -> Self; fn parser(&self) -> &Option; - fn to_block(&self) -> PdlBlock; + fn to_block(&self) -> Body; fn result_for(&self, output_results: Vec) -> PdlResult; - fn messages_for(&self, output_messages: Vec) -> Vec; + fn messages_for(&self, output_messages: &Vec) -> Vec; } /// Return the value of the last block if the list of blocks #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "lastOf")] pub struct LastOfBlock { /// Sequence of blocks to execute #[serde(rename = "lastOf")] pub last_of: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub role: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub defs: Option>, - #[serde(skip_serializing_if = "Option::is_none")] pub parser: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub def: Option, } impl SequencingBlock for LastOfBlock { fn kind(&self) -> &str { "lastOf" } - fn description(&self) -> &Option { - &self.description - } fn role(&self) -> &Option { &self.role } - fn def(&self) -> &Option { - return &self.def; - } - fn defs(&self) -> &Option> { - &self.defs - } fn items(&self) -> &Vec { &self.last_of } @@ -138,8 +178,8 @@ impl SequencingBlock for LastOfBlock { fn parser(&self) -> &Option { &self.parser } - fn to_block(&self) -> PdlBlock { - PdlBlock::LastOf(self.clone()) + fn to_block(&self) -> Body { + Body::LastOf(self.clone()) } fn result_for(&self, output_results: Vec) -> PdlResult { match output_results.last() { @@ -147,7 +187,7 @@ impl SequencingBlock for LastOfBlock { None => "".into(), } } - fn messages_for(&self, output_messages: Vec) -> Vec { + fn messages_for(&self, output_messages: &Vec) -> Vec { match output_messages.last() { Some(m) => vec![m.clone()], None => vec![], @@ -157,42 +197,29 @@ impl SequencingBlock for LastOfBlock { /// Create the concatenation of the stringify version of the result of /// each block of the list of blocks. -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, derive_builder::Builder)] +#[serde(tag = "kind", rename = "text")] +#[builder(setter(into, strip_option), default)] pub struct TextBlock { /// Body of the text + // Note: do NOT apply #[serde(default)] here. This seems to give + // permission for the deserializer to match everything to + // TextBlock, since ... all fields are optional/have defaults. pub text: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub role: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub defs: Option>, - - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub parser: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub def: Option, } impl SequencingBlock for TextBlock { fn kind(&self) -> &str { "text" } - fn description(&self) -> &Option { - &self.description - } fn role(&self) -> &Option { &self.role } - fn def(&self) -> &Option { - return &self.def; - } - fn defs(&self) -> &Option> { - &self.defs - } fn items(&self) -> &Vec { &self.text } @@ -204,62 +231,28 @@ impl SequencingBlock for TextBlock { fn parser(&self) -> &Option { &self.parser } - fn to_block(&self) -> PdlBlock { - PdlBlock::Text(self.clone()) + fn to_block(&self) -> Body { + Body::Text(self.clone()) } fn result_for(&self, output_results: Vec) -> PdlResult { PdlResult::String( output_results .into_iter() - .map(|m| m.to_string()) + .map(|m| match m { + PdlResult::String(s) => s, + x => x.to_string(), + }) .collect::>() .join("\n"), ) } - fn messages_for(&self, output_messages: Vec) -> Vec { - output_messages - } -} - -impl TextBlock { - pub fn new(text: Vec) -> Self { - TextBlock { - def: None, - defs: None, - description: None, - role: None, - parser: None, - text: text, - } - } - - pub fn def(&mut self, def: &str) -> &mut Self { - self.def = Some(def.into()); - self - } - - pub fn description(&mut self, description: String) -> &mut Self { - self.description = Some(description); - self - } - - pub fn parser(&mut self, parser: PdlParser) -> &mut Self { - self.parser = Some(parser); - self - } - - pub fn build(&self) -> Self { - self.clone() - } -} - -impl From> for TextBlock { - fn from(v: Vec) -> Self { - TextBlock::new(v).build() + fn messages_for(&self, output_messages: &Vec) -> Vec { + output_messages.clone() } } #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "function")] pub struct FunctionBlock { pub function: HashMap, #[serde(rename = "return")] @@ -268,70 +261,43 @@ pub struct FunctionBlock { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PdlUsage { - // Completion tokens consumed + /// Completion tokens consumed pub completion_tokens: u64, - // Prompt tokens consumed + /// Prompt tokens consumed pub prompt_tokens: u64, - // Completion nanos - pub completion_nanos: u64, - // Prompt nanos - pub prompt_nanos: u64, + /// Completion nanos + #[serde(skip_serializing_if = "Option::is_none")] + pub completion_nanos: Option, + /// Prompt nanos + #[serde(skip_serializing_if = "Option::is_none")] + pub prompt_nanos: Option, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, derive_builder::Builder)] +#[serde(tag = "kind", rename = "model")] +#[builder(setter(into, strip_option), default)] pub struct ModelBlock { - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - pub model: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub def: Option, + pub model: EvalsTo, #[serde(skip_serializing_if = "Option::is_none")] pub parameters: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub input: Option>, #[serde(skip_serializing_if = "Option::is_none")] + pub platform: Option, + #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "modelResponse")] pub model_response: Option, - #[serde(rename = "pdl__result")] - #[serde(skip_serializing_if = "Option::is_none")] - pub pdl_result: Option, #[serde(rename = "pdl__usage")] #[serde(skip_serializing_if = "Option::is_none")] pub pdl_usage: Option, -} - -impl ModelBlock { - pub fn new(model: &str) -> Self { - ModelBlock { - def: None, - description: None, - model_response: None, - parameters: None, - pdl_result: None, - pdl_usage: None, - model: model.into(), - input: None, - } - } - pub fn input(&mut self, input: PdlBlock) -> &mut Self { - self.input = Some(Box::new(input)); - self - } + /// The result of evaluating the `input` field (if given) + #[serde(rename = "pdl__model_input", skip_serializing_if = "Option::is_none")] + pub pdl_model_input: Option>, - pub fn input_str(&mut self, input: &str) -> &mut Self { - self.input = Some(Box::new(PdlBlock::String(input.into()))); - self - } - - pub fn parameters(&mut self, parameters: &HashMap) -> &mut Self { - self.parameters = Some(parameters.clone()); - self - } - - pub fn build(&self) -> Self { - self.clone() - } + /// The actual input given to the model (whether via `input` or from the incoming messages) + #[serde(skip_serializing_if = "Option::is_none")] + pub context: Option>, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -352,10 +318,11 @@ pub enum ListOrString { /// "${ name }'s number is ${ number }\\n" /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "repeat")] pub struct RepeatBlock { /// Arrays to iterate over #[serde(rename = "for")] - pub for_: HashMap, + pub for_: HashMap>>, /// Body of the loop pub repeat: Box, @@ -363,6 +330,7 @@ pub struct RepeatBlock { /// Create a message #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "message")] pub struct MessageBlock { /// Role of associated to the message, e.g. User or Assistant pub role: Role, @@ -370,8 +338,9 @@ pub struct MessageBlock { /// Content of the message pub content: Box, + /// pdl_id of block that defined the `content of this message #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, + pub defsite: Option, /// For example, the name of the tool that was invoked, for which this message is the tool response #[serde(skip_serializing_if = "Option::is_none")] @@ -386,6 +355,7 @@ pub struct MessageBlock { /// block. If the body of the object is an array, the resulting object /// is the union of the objects computed by each element of the array. #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "object")] pub struct ObjectBlock { pub object: HashMap, } @@ -413,6 +383,7 @@ pub struct ObjectBlock { /// def: EXTRACTED_GROUND_TRUTH /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "data")] pub struct DataBlock { pub data: Value, @@ -420,9 +391,6 @@ pub struct DataBlock { #[serde(skip_serializing_if = "Option::is_none")] pub raw: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub def: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub parser: Option, } @@ -438,8 +406,12 @@ pub struct DataBlock { /// result = random.randint(1, 20) /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "code")] pub struct PythonCodeBlock { + /// Programming language of the code pub lang: String, + + /// Code to execute pub code: String, } @@ -464,6 +436,7 @@ pub enum StringOrNull { /// parser: yaml /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "read")] pub struct ReadBlock { /// Name of the file to read. If `None`, read the standard input. pub read: StringOrNull, @@ -474,9 +447,6 @@ pub struct ReadBlock { /// Indicate if one or multiple lines should be read. pub multiline: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub def: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub parser: Option, } @@ -488,6 +458,40 @@ pub enum StringOrBoolean { Boolean(bool), } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Expr { + #[serde(rename = "pdl__expr")] + pub pdl_expr: S, + + #[serde(rename = "pdl__result", skip_serializing_if = "Option::is_none")] + pub pdl_result: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum EvalsTo { + Jinja(String), + Const(T), + Expr(Expr), +} + +impl Default for EvalsTo { + fn default() -> Self { + EvalsTo::Const("".to_string()) + } +} + +impl From<&str> for EvalsTo { + fn from(s: &str) -> Self { + EvalsTo::Const(s.to_string()) + } +} +impl From for EvalsTo { + fn from(s: String) -> Self { + EvalsTo::Const(s) + } +} + /// Conditional control structure. /// /// Example: @@ -500,10 +504,11 @@ pub enum StringOrBoolean { /// then: You won! /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "if")] pub struct IfBlock { /// The condition to check #[serde(rename = "if")] - pub condition: StringOrBoolean, + pub condition: EvalsTo, /// Branch to execute if the condition is true pub then: Box, @@ -514,11 +519,13 @@ pub struct IfBlock { pub else_: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub defs: Option>, + pub if_result: Option, } /// Return the array of values computed by each block of the list of blocks -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Default, derive_builder::Builder)] +#[serde(tag = "kind", rename = "array")] +#[builder(setter(into, strip_option), default)] pub struct ArrayBlock { /// Elements of the array pub array: Vec, @@ -526,30 +533,62 @@ pub struct ArrayBlock { /// Include a PDL file #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "include")] pub struct IncludeBlock { /// Name of the file to include. pub include: String, + + #[serde(rename = "pdl__trace", skip_serializing_if = "Option::is_none")] + pub pdl_trace: Option>, } /// Import a PDL file #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "import")] pub struct ImportBlock { /// Name of the file to include. pub import: String, + + #[serde(rename = "pdl__trace", skip_serializing_if = "Option::is_none")] + pub pdl_trace: Option>, } /// Block containing only defs #[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "kind", rename = "empty")] pub struct EmptyBlock { pub defs: IndexMap, } +/// A PDL program/sub-program consists of either a literal (string, number, boolean) or some kind of structured block #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] pub enum PdlBlock { Bool(bool), Number(Number), String(String), + Function(FunctionBlock), + Advanced(Block), + + // Must be last to prevent serde from aggressively matching on it, + // since other block types also (may) have a `defs`. + Empty(EmptyBlock), +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Block { + #[serde(flatten)] + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, + + #[serde(flatten)] + pub body: Body, +} + +/// A PDL block that has structure and metadata +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum Body { If(IfBlock), Import(ImportBlock), Include(IncludeBlock), @@ -559,15 +598,17 @@ pub enum PdlBlock { Array(ArrayBlock), Message(MessageBlock), Repeat(RepeatBlock), - Text(TextBlock), - LastOf(LastOfBlock), - Model(ModelBlock), - Function(FunctionBlock), PythonCode(PythonCodeBlock), Read(ReadBlock), + Model(ModelBlock), + LastOf(LastOfBlock), + Text(TextBlock), +} - // must be last to prevent serde from aggressively matching on it, since other block types also (may) have a `defs` - Empty(EmptyBlock), +impl From for PdlBlock { + fn from(b: bool) -> Self { + PdlBlock::Bool(b) + } } impl From<&str> for PdlBlock { @@ -602,8 +643,8 @@ pub enum PdlResult { Number(Number), String(String), Bool(bool), - Block(PdlBlock), Closure(Closure), + Block(PdlBlock), List(Vec), Dict(HashMap), } diff --git a/pdl-live-react/src-tauri/src/pdl/extract.rs b/pdl-live-react/src-tauri/src/pdl/extract.rs index 640915e8e..5b9d34514 100644 --- a/pdl-live-react/src-tauri/src/pdl/extract.rs +++ b/pdl-live-react/src-tauri/src/pdl/extract.rs @@ -1,16 +1,16 @@ -use yaml_rust2::Yaml; +use crate::pdl::ast::{ + Block, Body::*, EvalsTo, Expr, Metadata, ModelBlock, PdlBlock, PdlBlock::Advanced, +}; /// Extract models referenced by the programs -pub fn extract_models(programs: Vec) -> Vec { - extract_values(programs, "model") +pub fn extract_models(program: &PdlBlock) -> Vec { + extract_values(program, "model") } /// Take a list of Yaml fragments and produce a vector of the string-valued entries of the given field -pub fn extract_values(programs: Vec, field: &str) -> Vec { - let mut values = programs - .into_iter() - .flat_map(|p| extract_one_values(p, field)) - .collect::>(); +pub fn extract_values(program: &PdlBlock, field: &str) -> Vec { + let mut values = vec![]; + extract_values_iter(program, field, &mut values); // A single program may specify the same model more than once. Dedup! values.sort(); @@ -20,38 +20,90 @@ pub fn extract_values(programs: Vec, field: &str) -> Vec { } /// Take one Yaml fragment and produce a vector of the string-valued entries of the given field -fn extract_one_values(program: Yaml, field: &str) -> Vec { - let mut values: Vec = Vec::new(); - +fn extract_values_iter(program: &PdlBlock, field: &str, values: &mut Vec) { match program { - Yaml::Hash(h) => { - for (key, val) in h { - match key { - Yaml::String(f) if f == field => match &val { - Yaml::String(m) => { - values.push(m.to_string()); - } - _ => {} - }, - _ => {} - } - - for m in extract_one_values(val, field) { - values.push(m) - } - } + PdlBlock::Empty(b) => { + b.defs + .values() + .for_each(|p| extract_values_iter(p, field, values)); } - - Yaml::Array(a) => { - for val in a { - for m in extract_one_values(val, field) { - values.push(m) - } + PdlBlock::Function(b) => { + extract_values_iter(&b.return_, field, values); + } + Advanced(Block { + body: + Model(ModelBlock { + model: EvalsTo::::Const(m), + .. + }), + .. + }) => values.push(m.clone()), + Advanced(Block { + body: + Model(ModelBlock { + model: EvalsTo::::Jinja(m), + .. + }), + .. + }) => values.push(m.clone()), + Advanced(Block { + body: + Model(ModelBlock { + model: EvalsTo::::Expr(Expr { pdl_expr: m, .. }), + .. + }), + .. + }) => values.push(m.clone()), + Advanced(Block { + body: Repeat(b), .. + }) => { + extract_values_iter(&b.repeat, field, values); + } + Advanced(Block { + body: Message(b), .. + }) => { + extract_values_iter(&b.content, field, values); + } + Advanced(Block { body: Array(b), .. }) => b + .array + .iter() + .for_each(|p| extract_values_iter(p, field, values)), + Advanced(Block { body: Text(b), .. }) => { + b.text + .iter() + .for_each(|p| extract_values_iter(p, field, values)); + } + Advanced(Block { + body: LastOf(b), .. + }) => { + b.last_of + .iter() + .for_each(|p| extract_values_iter(p, field, values)); + } + Advanced(Block { body: If(b), .. }) => { + extract_values_iter(&b.then, field, values); + if let Some(else_) = &b.else_ { + extract_values_iter(else_, field, values); } } + Advanced(Block { + body: Object(b), .. + }) => b + .object + .values() + .for_each(|p| extract_values_iter(p, field, values)), _ => {} } - values + if let Advanced(Block { + metadata: Some(Metadata { + defs: Some(defs), .. + }), + .. + }) = program + { + defs.values() + .for_each(|p| extract_values_iter(p, field, values)); + } } diff --git a/pdl-live-react/src-tauri/src/pdl/interpreter.rs b/pdl-live-react/src-tauri/src/pdl/interpreter.rs index e695f87fd..81d13cfe6 100644 --- a/pdl-live-react/src-tauri/src/pdl/interpreter.rs +++ b/pdl-live-react/src-tauri/src/pdl/interpreter.rs @@ -1,54 +1,125 @@ -// use ::std::cell::LazyCell; use ::std::collections::HashMap; -use ::std::env::current_dir; use ::std::error::Error; -use ::std::fs::{read_to_string as read_file_to_string, File}; use ::std::path::PathBuf; -use std::sync::{Arc, Mutex}; use async_recursion::async_recursion; -use minijinja::{syntax::SyntaxConfig, Environment}; +use minijinja::{Environment, syntax::SyntaxConfig}; use owo_colors::OwoColorize; -use tokio::io::{stdout, AsyncWriteExt}; +use tokio::io::{AsyncWriteExt, stdout}; use tokio_stream::StreamExt; use ollama_rs::{ + Ollama, generation::{ - chat::{request::ChatMessageRequest, ChatMessage, ChatMessageResponse, MessageRole}, - tools::ToolInfo, + chat::{ChatMessage, ChatMessageResponse, MessageRole, request::ChatMessageRequest}, + tools::{ToolFunctionInfo, ToolInfo, ToolType}, }, models::ModelOptions, - Ollama, }; -use serde_json::{from_str, to_string, Value}; +use serde_json::{Value, from_str, json, to_string}; use serde_norway::{from_reader, from_str as from_yaml_str}; use crate::pdl::ast::{ - ArrayBlock, CallBlock, Closure, DataBlock, EmptyBlock, FunctionBlock, IfBlock, ImportBlock, - IncludeBlock, ListOrString, MessageBlock, ModelBlock, ObjectBlock, PdlBlock, PdlParser, - PdlResult, PdlUsage, PythonCodeBlock, ReadBlock, RepeatBlock, Role, Scope, SequencingBlock, - StringOrBoolean, StringOrNull, + ArrayBlock, Block, + Body::{self, *}, + CallBlock, Closure, DataBlock, EmptyBlock, EvalsTo, Expr, FunctionBlock, IfBlock, ImportBlock, + IncludeBlock, ListOrString, MessageBlock, Metadata, MetadataBuilder, ModelBlock, ObjectBlock, + PdlBlock, + PdlBlock::Advanced, + PdlParser, PdlResult, PdlUsage, PythonCodeBlock, ReadBlock, RepeatBlock, Role, Scope, + SequencingBlock, StringOrBoolean, StringOrNull, Timing, }; -type Context = Vec; -type PdlError = Box; -type Interpretation = Result<(PdlResult, Context, PdlBlock), PdlError>; -type InterpretationSync = Result<(PdlResult, Context, PdlBlock), Box>; +type Messages = Vec; +type ThreadSafeError = dyn Error + Send + Sync; +type PdlError = Box; +type Interpretation = Result<(PdlResult, Messages, PdlBlock), PdlError>; +type BodyInterpretation = Result<(PdlResult, Messages, Body), PdlError>; +type InterpretationSync = Result<(PdlResult, Messages, PdlBlock), Box>; + +pub struct RunOptions<'a> { + pub stream: bool, + pub debug: bool, + pub trace: Option<&'a str>, +} + +impl<'a> Default for RunOptions<'a> { + fn default() -> Self { + Self { + stream: true, + debug: false, + trace: None, + } + } +} + +#[derive(Clone)] +struct State { + emit: bool, + cwd: PathBuf, + scope: Scope, + escaped_variables: Vec, + messages: Messages, + id_stack: Vec, +} + +impl State { + fn new(initial_scope: Scope) -> Self { + Self { + emit: true, + cwd: ::std::env::current_dir().unwrap_or(PathBuf::from("/")), + scope: initial_scope, + escaped_variables: vec![], + messages: vec![], + id_stack: vec![], + } + } + + fn id(&self) -> String { + self.id_stack.join(".") + } + + fn with_cwd(&self, cwd: PathBuf) -> Self { + let mut s = self.clone(); + s.cwd = cwd; + s + } + + fn with_emit(&self, emit: bool) -> Self { + let mut s = self.clone(); + s.emit = emit; + s + } + + fn with_iter(&self, iter: usize) -> Self { + let mut s = self.clone(); + s.id_stack.push(format!("{iter}")); + s + } + + fn incr_iter(&self, iter: usize) -> Self { + let mut s = self.clone(); + s.id_stack.pop(); + s.id_stack.push(format!("{iter}")); + s + } + + fn extend_scope(&self, scopes: Vec) -> Self { + let mut s = self.clone(); + scopes.into_iter().for_each(|m| s.scope.extend(m)); + s + } +} struct Interpreter<'a> { // batch: u32, - // role: Role, - cwd: PathBuf, - // id_stack: Vec, + options: RunOptions<'a>, jinja_env: Environment<'a>, - scope: Vec, - debug: bool, - emit: bool, } impl<'a> Interpreter<'a> { - fn new() -> Self { + fn new(options: RunOptions<'a>) -> Self { let mut jinja_env = Environment::new(); // PDL uses custom variable delimeters, because {{ }} have pre-defined meaning in yaml jinja_env.set_syntax( @@ -60,106 +131,242 @@ impl<'a> Interpreter<'a> { Self { // batch: 0, - // role: Role::User, - cwd: current_dir().unwrap_or(PathBuf::from("/")), // id_stack: vec![], jinja_env: jinja_env, - scope: vec![Scope::new()], - debug: false, - emit: true, + options: options, } } - async fn run_with_emit( + async fn _run_with_state( &mut self, program: &PdlBlock, - context: Context, - emit: bool, + state: &mut State, + parent_scope: &mut Scope, ) -> Interpretation { - if self.debug { - if let Some(scope) = self.scope.last() { - if scope.len() > 0 { - eprintln!("Run with Scope {:?}", scope); - } - } - } - - let prior_emit = self.emit; - self.emit = emit; - - let (result, messages, trace) = match program { + let res = match program { + PdlBlock::Bool(b) => Ok(( + b.into(), + vec![ChatMessage::user(format!("{b}"))], + PdlBlock::Bool(b.clone()), + )), PdlBlock::Number(n) => Ok(( n.clone().into(), vec![ChatMessage::user(format!("{n}"))], PdlBlock::Number(n.clone()), )), PdlBlock::Function(f) => Ok(( - PdlResult::Closure(self.closure(&f)), + PdlResult::Closure(self.closure(&f, state)), vec![], PdlBlock::Function(f.clone()), )), - PdlBlock::String(s) => self.run_string(s, context).await, - PdlBlock::Call(block) => self.run_call(block, context).await, - PdlBlock::Empty(block) => self.run_empty(block, context).await, - PdlBlock::If(block) => self.run_if(block, context).await, - PdlBlock::Import(block) => self.run_import(block, context).await, - PdlBlock::Include(block) => self.run_include(block, context).await, - PdlBlock::Model(block) => self.run_model(block, context).await, - PdlBlock::Data(block) => self.run_data(block, context).await, - PdlBlock::Object(block) => self.run_object(block, context).await, - PdlBlock::PythonCode(block) => self.run_python_code(block, context).await, - PdlBlock::Read(block) => self.run_read(block, context).await, - PdlBlock::Repeat(block) => self.run_repeat(block, context).await, - PdlBlock::LastOf(block) => self.run_sequence(block, context).await, - PdlBlock::Text(block) => self.run_sequence(block, context).await, - PdlBlock::Array(block) => self.run_array(block, context).await, - PdlBlock::Message(block) => self.run_message(block, context).await, - _ => Err(Box::from(format!("Unsupported block {:?}", program))), + PdlBlock::Empty(b) => self.run_empty(b, state).await, + PdlBlock::String(s) => self.run_string(s, state).await, + Advanced(b) => self.run_advanced(b, state).await, }?; - if match program { - PdlBlock::Call(_) | PdlBlock::Model(_) => false, - _ => self.emit, + // copy any escaped variable bindings to the parent scope + parent_scope.extend( + state + .escaped_variables + .iter() + .filter_map(|variable| state.scope.remove_entry(&variable.clone())), + ); + + if match &program { + Advanced(Block { + body: Message(_), .. + }) + | Advanced(Block { body: Text(_), .. }) + | Advanced(Block { + body: Import(_), .. + }) + | Advanced(Block { + body: Include(_), .. + }) + | Advanced(Block { + body: LastOf(_), .. + }) + | Advanced(Block { body: Call(_), .. }) + | Advanced(Block { body: Model(_), .. }) => false, + _ => state.emit, } { - println!("{}", pretty_print(&messages)); + println!("{}", pretty_print(&res.1)); } - self.emit = prior_emit; - Ok((result, messages, trace)) + Ok(res) + } + + async fn run_advanced(&mut self, block: &Block, state: &mut State) -> Interpretation { + let mut timing = Timing::start()?; + + // This is just so we can avoid Option in the run_* + // functions. We pass in an immutable reference, so no harm in + // the or_default() part. + let m = &block.metadata.clone().unwrap_or_default(); + + self.process_defs(&m.defs, state).await?; + + let (result, messages, trace_body) = match &block.body { + Call(b) => { + state.id_stack.push("call".to_string()); + self.run_call(b, m, state).await + } + Data(b) => { + state.id_stack.push("data".to_string()); + self.run_data(b, m, state).await + } + If(b) => { + state.id_stack.push("if".to_string()); + self.run_if(b, m, state).await + } + Import(b) => { + state.id_stack.push("import".to_string()); + self.run_import(b, m, state).await + } + Include(b) => { + state.id_stack.push("include".to_string()); + self.run_include(b, m, state).await + } + Model(b) => { + state.id_stack.push("model".to_string()); + self.run_model(b, m, state).await + } + Object(b) => { + state.id_stack.push("object".to_string()); + self.run_object(b, m, state).await + } + PythonCode(b) => { + state.id_stack.push("code".to_string()); + self.run_python_code(b, m, state).await + } + Read(b) => { + state.id_stack.push("read".to_string()); + self.run_read(b, m, state).await + } + Repeat(b) => { + state.id_stack.push("repeat".to_string()); + self.run_repeat(b, m, state).await + } + LastOf(b) => { + state.id_stack.push("lastOf".to_string()); + self.run_sequence(b, m, state).await + } + Text(b) => { + state.id_stack.push("text".to_string()); + self.run_sequence(b, m, state).await + } + Array(b) => { + state.id_stack.push("array".to_string()); + self.run_array(b, m, state).await + } + Message(b) => { + state.id_stack.push("message".to_string()); + self.run_message(b, m, state).await + } + }?; + + let mut trace = Block { + metadata: block.metadata.clone(), + body: trace_body, + }; + + timing.end()?; + + let mut trace_metadata = m.clone(); + trace_metadata.pdl_id = Some(state.id()); + trace_metadata.pdl_timing = Some(timing); + trace_metadata.pdl_result = Some(Box::new(result.clone())); + trace.metadata = Some(trace_metadata); + + state.id_stack.pop(); + + Ok((result, messages, Advanced(trace))) } #[async_recursion] - async fn run_quiet(&mut self, program: &PdlBlock, context: Context) -> Interpretation { - self.run_with_emit(program, context, false).await + async fn run_quiet(&mut self, program: &PdlBlock, state: &mut State) -> Interpretation { + self._run_with_state(program, &mut state.with_emit(false), &mut state.scope) + .await } #[async_recursion] - async fn run(&mut self, program: &PdlBlock, context: Context) -> Interpretation { - self.run_with_emit(program, context, self.emit).await + async fn run(&mut self, program: &PdlBlock, state: &mut State) -> Interpretation { + self._run_with_state(program, &mut state.with_emit(true), &mut state.scope) + .await } /// Evaluate String as a Jinja2 expression - fn eval(&self, expr: &String) -> Result { - let result = self - .jinja_env - .render_str(expr.as_str(), self.scope.last().unwrap_or(&HashMap::new()))?; - if self.debug { - eprintln!("Eval {} -> {}", expr, result); + fn eval(&self, expr: &String, state: &State) -> Result { + let tmpl = self.jinja_env.template_from_str(expr.as_str())?; + let result = tmpl.render(&state.scope)?; + if self.options.debug { + eprintln!("Eval {} -> {} with scope {:?}", expr, result, state.scope); } let backup = result.clone(); Ok(from_str(&result).unwrap_or_else(|err| { - if self.debug { - eprintln!("Treating as plain string {}", &result); + if self.options.debug { + eprintln!("Treating as plain string {}", result); eprintln!("... due to {}", err); } backup.into() })) } + fn eval_string( + &self, + expr: &EvalsTo>, + state: &State, + ) -> Result { + match expr { + EvalsTo::Const(t) => Ok(*t.clone()), + EvalsTo::Jinja(s) => self.eval(s, state), + EvalsTo::Expr(e) => self.eval(&e.pdl_expr, state), + } + } + + // TODO how can we better cope with the expected String return? + fn eval_string_to_string( + &self, + expr: &EvalsTo, + state: &State, + ) -> Result { + match expr { + EvalsTo::Const(s) | EvalsTo::Jinja(s) => self.eval(s, state), + EvalsTo::Expr(e) => self.eval(&e.pdl_expr, state), + } + } + + /// Evaluate an Expr to a bool + fn eval_to_bool( + &self, + expr: &EvalsTo, + state: &State, + ) -> Result { + match expr { + EvalsTo::Const(b) + | EvalsTo::Expr(Expr { + pdl_expr: StringOrBoolean::Boolean(b), + .. + }) => Ok(b.clone()), + + EvalsTo::Jinja(s) + | EvalsTo::Expr(Expr { + pdl_expr: StringOrBoolean::String(s), + .. + }) => match self.eval(s, state)? { + PdlResult::Bool(b) => Ok(b.clone()), + x => Err(Box::from(format!( + "Expression {s} evaluated to non-boolean {:?}", + x + ))), + }, + } + } + /// Evaluate String as a Jinja2 expression, expecting a string in response - fn eval_to_string(&self, expr: &String) -> Result { - match self.eval(expr)? { + fn eval_to_string(&self, expr: &String, state: &State) -> Result { + match self.eval(expr, state)? { PdlResult::String(s) => Ok(s), x => Err(Box::from(format!( "Expression {expr} evaluated to non-string {:?}", @@ -169,20 +376,20 @@ impl<'a> Interpreter<'a> { } /// Traverse the given JSON Value, applying `self.eval()` to the value elements within. - fn eval_json(&self, expr: &Value) -> Result { + fn eval_json(&self, expr: &Value, state: &State) -> Result { match expr { Value::Null => Ok("".into()), Value::Bool(b) => Ok(PdlResult::Bool(*b)), Value::Number(n) => Ok(PdlResult::Number(n.clone())), - Value::String(s) => self.eval(s), + Value::String(s) => self.eval(s, state), Value::Array(a) => Ok(PdlResult::List( a.iter() - .map(|v| self.eval_json(v)) + .map(|v| self.eval_json(v, state)) .collect::>()?, )), Value::Object(o) => Ok(PdlResult::Dict( o.iter() - .map(|(k, v)| match self.eval_json(v) { + .map(|(k, v)| match self.eval_json(v, state) { Ok(v) => Ok((k.clone(), v)), Err(e) => Err(e), }) @@ -192,46 +399,67 @@ impl<'a> Interpreter<'a> { } /// Evaluate an string or list of Values into a list of Values - fn eval_list_or_string(&self, expr: &ListOrString) -> Result, PdlError> { + fn eval_list_or_string( + &self, + expr: &EvalsTo>, + state: &State, + ) -> Result, PdlError> { match expr { - ListOrString::String(s) => match self.eval(s)? { - PdlResult::List(a) => Ok(a), + EvalsTo::Const(c) => Ok(c.clone()), + EvalsTo::Jinja(s) + | EvalsTo::Expr(Expr { + pdl_expr: ListOrString::String(s), + .. + }) => match self.eval(s, state)? { + PdlResult::List(l) => Ok(l), x => Err(Box::from(format!( - "Jinja string expanded to non-list. {} -> {:?}", - s, x + "Expression {s} evaluated to non-list {:?}", + x ))), }, - ListOrString::List(l) => l.iter().map(|v| self.eval_json(v)).collect(), + EvalsTo::Expr(Expr { + pdl_expr: ListOrString::List(l), + .. + }) => l.iter().map(|v| self.eval_json(v, state)).collect(), } } /// Create a closure for the given function `f` - fn closure(&self, f: &FunctionBlock) -> Closure { + fn closure(&self, f: &FunctionBlock, state: &State) -> Closure { Closure { function: f.clone(), - scope: self.scope.last().unwrap_or(&HashMap::new()).clone(), + scope: state.scope.clone(), } } /// Run a PdlBlock::String - async fn run_string(&self, msg: &String, _context: Context) -> Interpretation { - let trace = self.eval(msg)?; - if self.debug { - eprintln!("String {} -> {:?}", msg, trace); + async fn run_string(&self, msg: &String, state: &State) -> Interpretation { + let result = self.eval(msg, state)?; + if self.options.debug { + eprintln!("String {} -> {:?}", msg, result); } - let result_string = match &trace { + let result_string = match &result { PdlResult::String(s) => s.clone(), x => to_string(&x)?, }; + let messages = vec![ChatMessage::user(result_string)]; + let trace = Advanced(Block { + metadata: Some(MetadataBuilder::default().pdl_id(state.id()).build()?), + body: Data(DataBlock { + data: json!({ "pdl__expr": msg.clone(), "pdl__result": result.clone() }), + parser: None, + raw: None, + }), + }); - Ok((trace, messages, PdlBlock::String(msg.clone()))) + Ok((result, messages, trace)) } - /// If `file_path` is not absolute, join it with self.cwd - fn path_to(&self, file_path: &String) -> PathBuf { - let mut path = self.cwd.clone(); + /// If `file_path` is not absolute, join it with state.cwd + fn path_to(&self, file_path: &String, state: &State) -> PathBuf { + let mut path = state.cwd.clone(); path.push(file_path); if path.extension().is_none() { path.with_extension("pdl") @@ -245,6 +473,8 @@ impl<'a> Interpreter<'a> { variable: &Option, value: &PdlResult, parser: &Option, + state: &mut State, + escape: bool, ) -> Result { let result = if let Some(parser) = parser { if let PdlResult::String(s) = value { @@ -261,11 +491,14 @@ impl<'a> Interpreter<'a> { }?; if let Some(def) = &variable { - if let Some(scope) = self.scope.last_mut() { - if self.debug { - eprintln!("Def {} -> {}", def, result); - } - scope.insert(def.clone(), result.clone()); + if self.options.debug { + eprintln!("Def {} -> {}", def, result); + } + state.scope.insert(def.clone(), result.clone()); + + // then we want this binding to escape to the parent scope + if escape { + state.escaped_variables.push(def.clone()); } } @@ -273,20 +506,32 @@ impl<'a> Interpreter<'a> { } /// Run a PdlBlock::Read - async fn run_read(&mut self, block: &ReadBlock, _context: Context) -> Interpretation { + async fn run_read( + &mut self, + block: &ReadBlock, + metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { let trace = block.clone(); - println!( - "{}", - match (&block.message, block.multiline) { - (Some(message), _) => message.as_str(), - (None, Some(true)) => "Enter/Paste your content. Ctrl-D to save it.", - _ => "How can i help you?", + match (&block.read, &block.message) { + (StringOrNull::String(_), None) => {} // read from file and no explicit message... then don't print a message + _ => { + println!( + "{}", + match (&block.message, block.multiline) { + (Some(message), _) => message.as_str(), + (None, Some(true)) => "Enter/Paste your content. Ctrl-D to save it.", + _ => "How can i help you?", + } + ); } - ); + } let buffer = match &block.read { - StringOrNull::String(file_path) => read_file_to_string(self.path_to(file_path))?, + StringOrNull::String(file_path) => { + ::std::fs::read_to_string(self.path_to(file_path, state))? + } StringOrNull::Null => { let mut buffer = String::new(); let mut bytes_read = ::std::io::stdin().read_line(&mut buffer)?; @@ -299,122 +544,150 @@ impl<'a> Interpreter<'a> { } }; - let result = self.def(&block.def, &buffer.clone().into(), &block.parser)?; + let result = self.def( + &metadata.def, + &buffer.clone().into(), + &block.parser, + state, + true, + )?; - Ok(( - result, - vec![ChatMessage::user(buffer)], - PdlBlock::Read(trace), - )) + Ok((result, vec![ChatMessage::user(buffer)], Read(trace))) } /// Run a PdlBlock::Call - async fn run_call(&mut self, block: &CallBlock, context: Context) -> Interpretation { - if self.debug { + async fn run_call( + &mut self, + block: &CallBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { eprintln!("Call {:?}({:?})", block.call, block.args); - eprintln!("Call scope {:?}", self.scope.last()); + eprintln!("Call scope {:?}", state.scope); } - let res = match self.eval(&block.call)? { + match self.eval_string(&block.call, state)? { PdlResult::Closure(c) => { - if let Some(args) = &block.args { - match self.eval_json(args)? { - PdlResult::Dict(m) => { - self.push_and_extend_scope_with(m, c.scope); - Ok(()) - } - x => Err(Box::::from(format!( - "Call arguments not a map: {:?}", - x - ))), - }?; - } - - self.run(&c.function.return_, context.clone()).await + let mut new_state = match &block.args { + None => Ok(state.clone()), + Some(args) => match self.eval_json(args, state)? { + PdlResult::Dict(m) => Ok(state.extend_scope(vec![m, c.scope])), + x => Err(PdlError::from(format!("Call arguments not a map: {:?}", x))), + }, + }?; + + let (result, messages, call_trace) = + self.run(&c.function.return_, &mut new_state).await?; + let mut trace = block.clone(); + trace.pdl_trace = Some(Box::new(call_trace)); + Ok((result, messages, Body::Call(trace))) } - _ => Err(Box::from(format!("call of non-function {:?}", &block.call))), - }; - - if let Some(_) = block.args { - self.scope.pop(); + x => Err(Box::from(format!( + "call of non-function {:?}->{:?}", + block.call, x + ))), } - - res } /// Run a PdlBlock::Empty - async fn run_empty(&mut self, block: &EmptyBlock, _context: Context) -> Interpretation { - if self.debug { + async fn run_empty(&mut self, block: &EmptyBlock, state: &mut State) -> Interpretation { + if self.options.debug { eprintln!("Empty"); } + self.process_defs(&Some(block.defs.clone()), state).await?; + let trace = block.clone(); - self.process_defs(&Some(block.defs.clone())).await?; Ok(( - PdlResult::Dict(self.scope.last().unwrap_or(&HashMap::new()).clone()), + PdlResult::Dict(state.scope.clone()), vec![], PdlBlock::Empty(trace), )) } /// Run a PdlBlock::Call - async fn run_if(&mut self, block: &IfBlock, context: Context) -> Interpretation { - if self.debug { + async fn run_if( + &mut self, + block: &IfBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { eprintln!("If {:?}({:?})", block.condition, block.then); } - self.process_defs(&block.defs).await?; - - let cond = match &block.condition { - StringOrBoolean::Boolean(b) => PdlResult::Bool(*b), - StringOrBoolean::String(s) => self.eval(s)?, - }; - let res = match cond { - PdlResult::Bool(true) => self.run_quiet(&block.then, context).await, - PdlResult::Bool(false) => match &block.else_ { - Some(else_block) => self.run_quiet(&else_block, context).await, - None => Ok(("".into(), vec![], PdlBlock::If(block.clone()))), - }, - x => Err(Box::from(format!( - "if block condition evaluated to non-boolean value: {:?}", - x - ))), + let mut trace = block.clone(); + let if_result = self.eval_to_bool(&block.condition, state)?; + trace.if_result = Some(if_result); + + let (result, messages) = if if_result { + let (result, messages, then_trace) = self.run_quiet(&block.then, state).await?; + trace.then = Box::new(then_trace); + (result, messages) + } else if let Some(else_block) = &block.else_ { + let (result, messages, else_trace) = self.run_quiet(&else_block, state).await?; + trace.else_ = Some(Box::new(else_trace)); + (result, messages) + } else { + ("".into(), vec![]) }; - self.scope.pop(); - res + Ok((result, messages, If(trace))) } /// Run a PdlBlock::Include - async fn run_include(&mut self, block: &IncludeBlock, context: Context) -> Interpretation { - if self.debug { + async fn run_include( + &mut self, + block: &IncludeBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { eprintln!("Include {:?}", block.include); } - let path = self.path_to(&block.include); - let old_cwd = self.cwd.clone(); - if let Some(cwd) = path.parent() { - self.cwd = cwd.to_path_buf() - } - let res = self.run_quiet(&parse_file(&path)?, context.clone()).await; - self.cwd = old_cwd; - res + let path = self.path_to(&block.include, state); + let mut new_state = if let Some(cwd) = path.parent() { + state.with_cwd(cwd.to_path_buf()) + } else { + state.clone() + }; + + let (result, messages, include_trace) = + self.run(&parse_file(&path)?, &mut new_state).await?; + + let mut trace = block.clone(); + trace.pdl_trace = Some(Box::new(include_trace)); + + Ok((result, messages, Include(trace))) } /// Run a PdlBlock::Import - async fn run_import(&mut self, block: &ImportBlock, context: Context) -> Interpretation { - if self.debug { + async fn run_import( + &mut self, + block: &ImportBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { eprintln!("Import {:?}", block.import); } - let path = self.path_to(&block.import); - let old_cwd = self.cwd.clone(); - if let Some(cwd) = path.parent() { - self.cwd = cwd.to_path_buf() - } - let res = self.run_quiet(&parse_file(&path)?, context.clone()).await; - self.cwd = old_cwd; - res + let path = self.path_to(&block.import, state); + let mut new_state = if let Some(cwd) = path.parent() { + state.with_cwd(cwd.to_path_buf()) + } else { + state.clone() + }; + + let (result, messages, import_trace) = + self.run(&parse_file(&path)?, &mut new_state).await?; + + let mut trace = block.clone(); + trace.pdl_trace = Some(Box::new(import_trace)); + + Ok((result, messages, Import(trace))) } fn to_ollama_model_options( @@ -438,10 +711,36 @@ impl<'a> Interpreter<'a> { 0.0 }; - let tools = if let Some(Value::Array(_tools)) = parameters.get(&"tools".to_string()) { - // TODO - //tools.into_iter().map(|tool| function!()).collect() - vec![] + let tools = if let Some(Value::Array(tools)) = parameters.get("tools") { + tools + .into_iter() + .filter_map(|tool| tool.get("function")) + .filter_map(|tool| { + //from_str(&to_string(tool)?) + match ( + tool.get("name"), + tool.get("description"), + tool.get("parameters"), + ) { + ( + Some(Value::String(name)), + Some(Value::String(description)), + Some(Value::Object(parameters)), + ) => Some(ToolInfo { + tool_type: ToolType::Function, + function: ToolFunctionInfo { + name: name.to_string(), + description: description.to_string(), + parameters: schemars::schema_for_value!(parameters), + }, + }), + _ => { + eprintln!("Error: tools do not satisfy schema {:?}", tool); + None + } + } + }) + .collect() } else { vec![] }; @@ -456,42 +755,77 @@ impl<'a> Interpreter<'a> { async fn run_python_code( &mut self, block: &PythonCodeBlock, - _context: Context, - ) -> Interpretation { + _metadata: &Metadata, + _state: &mut State, + ) -> BodyInterpretation { use rustpython_vm as vm; - vm::Interpreter::without_stdlib(Default::default()).enter(|vm| -> Interpretation { + + let mut settings = rustpython_vm::Settings::default(); + + // add PYTHONPATH to sys.path + settings.path_list.extend(get_paths("PDLPYTHONPATH")); + settings.path_list.extend(get_paths("PYTHONPATH")); + + if let Ok(venv) = ::std::env::var("VIRTUAL_ENV") { + let path = ::std::path::PathBuf::from(venv).join(if cfg!(windows) { + "lib/site-packages" + } else { + // TODO generalize this! + "lib/python3.12/site-packages" + }); + settings = settings.with_path(path.display().to_string()); + } + + let interp = vm::Interpreter::with_init(settings, |vm| { + vm.add_native_modules(rustpython_stdlib::get_module_inits()); + vm.add_frozen(rustpython_pylib::FROZEN_STDLIB); + }); + interp.enter(|vm| -> BodyInterpretation { let scope = vm.new_scope_with_builtins(); - // TODO vm.new_syntax_error(&err, Some(block.code.as_str())) - let code_obj = vm - .compile( - block.code.as_str(), - vm::compiler::Mode::Exec, - "".to_owned(), - ) - .map_err(|_err| { - panic!("Syntax error in Python code"); - }) - .unwrap(); + // Sigh, this is copy-pasted from RustPython/src/lib.rs + // `run_rustpython` as of 20250416 commit hash + // a917da3b1. Without this (and also: importlib and + // encodings features on rustpython-vm crate), then + // pulling in venvs does not work. + match vm.run_code_string( + vm.new_scope_with_builtins(), + "import sys; sys.path.insert(0, '')", + "".to_owned(), + ) { + Ok(_) => Ok(()), + Err(exc) => { + vm.print_exception(exc); + Err(PdlError::from("Error setting up Python site path")) + } + }?; + let site_result = vm.import("site", 0); + if site_result.is_err() { + println!( + "Failed to import site, consider adding the Lib directory to your RUSTPYTHONPATH \ + environment variable", + ); + } - let _output = vm - .run_code_obj(code_obj, scope.clone()) - .map_err(|_err| { - // TODO vm.print_exception(exc); - println!("Error executing Python code"); - }) - .unwrap(); + match vm.run_code_string(scope.clone(), block.code.as_str(), "".to_owned()) { + Ok(_) => Ok(()), + Err(exc) => { + vm.print_exception(exc); + Err(PdlError::from("Error executing Python code")) + } + }?; match scope.globals.get_item("result", vm) { Ok(result) => { - let result_string = result - .str(vm) - .map_err(|e| { - panic!("Unable to stringify Python 'result' value {:?}", e); - }) - .unwrap(); + let result_string = match result.str(vm) { + Ok(x) => Ok(x), + Err(exc) => { + vm.print_exception(exc); + Err(PdlError::from("Unable to stringify Python 'result' value")) + } + }?; let messages = vec![ChatMessage::user(result_string.as_str().to_string())]; - let trace = PdlBlock::PythonCode(block.clone()); + let trace = PythonCode(block.clone()); Ok((messages[0].content.clone().into(), messages, trace)) } Err(_) => Err(Box::from( @@ -501,167 +835,269 @@ impl<'a> Interpreter<'a> { }) } - /// Run a PdlBlock::Model - async fn run_model(&mut self, block: &ModelBlock, context: Context) -> Interpretation { - match &block.model { - pdl_model - if pdl_model.starts_with("ollama/") || pdl_model.starts_with("ollama_chat/") => - { - let ollama = Ollama::default(); - let model = if pdl_model.starts_with("ollama/") { - &pdl_model[7..] - } else { - &pdl_model[12..] - }; + async fn run_ollama_model( + &mut self, + pdl_model: String, + block: &ModelBlock, + metadata: &Metadata, + state: &mut State, + input_messages: Vec, + ) -> Result<(String, Option), PdlError> { + let mut ollama = Ollama::default(); + let model = if pdl_model.starts_with("ollama/") { + &pdl_model[7..] + } else { + &pdl_model[12..] + }; - let (options, tools) = self.to_ollama_model_options(&block.parameters); - if self.debug { - println!("Model options {:?}", options); - } + let (options, tools) = self.to_ollama_model_options(&block.parameters); + if self.options.debug { + eprintln!("Model options {:?} {:?}", metadata.description, options); + eprintln!("Model tools {:?} {:?}", metadata.description, tools); + } - let input_messages = match &block.input { - Some(input) => { - // TODO ignoring result, trace - let (_result, messages, _trace) = self.run_quiet(&*input, context).await?; - messages - } - None => context, - }; - let (prompt, history_slice): (&ChatMessage, &[ChatMessage]) = - match input_messages.split_last() { - Some(x) => x, - None => (&ChatMessage::user("".into()), &[]), - }; - let history = Vec::from(history_slice); - if self.debug { - eprintln!( - "Ollama {:?} model={:?} prompt={:?} history={:?}", - block.description.clone().unwrap_or("".into()), - block.model, - prompt, - history - ); - } + let (prompt, history_slice): (&ChatMessage, &[ChatMessage]) = + match input_messages.split_last() { + Some(x) => x, + None => (&ChatMessage::user("".into()), &[]), + }; + let mut history = Vec::from(history_slice); + if self.options.debug { + eprintln!( + "Ollama {:?} model={:?} prompt={:?} history={:?}", + metadata.description, block.model, prompt, history + ); + } - if self.emit { - println!("{}", pretty_print(&input_messages)); - } + let req = ChatMessageRequest::new(model.into(), vec![prompt.clone()]) + .options(options) + .tools(tools); + + let (last_res, response_string) = if !self.options.stream { + let res = ollama + .send_chat_messages_with_history(&mut history, req) + .await?; + let response_string = res.message.content.clone(); + print!("{}", response_string); + (Some(res), response_string) + } else { + let mut stream = ollama + .send_chat_messages_with_history_stream( + ::std::sync::Arc::new(::std::sync::Mutex::new(history)), + req, + //ollama.generate(GenerationRequest::new(model.into(), prompt), + ) + .await?; + // dbg!("Model result {:?}", &res); - let req = ChatMessageRequest::new(model.into(), vec![prompt.clone()]) - .options(options) - .tools(tools); - /* if we ever want non-streaming: - let res = ollama - .send_chat_messages_with_history( - &mut history, - req, - //ollama.generate(GenerationRequest::new(model.into(), prompt), - ) - .await?; - // dbg!("Model result {:?}", &res); + let emit = if let Some(_) = &block.model_response { + false + } else { + true + }; - let mut trace = block.clone(); - trace.pdl_result = Some(res.message.content.clone()); - - if let Some(usage) = res.final_data { - trace.pdl_usage = Some(PdlUsage { - prompt_tokens: usage.prompt_eval_count, - prompt_nanos: usage.prompt_eval_duration, - completion_tokens: usage.eval_count, - completion_nanos: usage.eval_duration, - }); - } - // dbg!(history); - Ok((vec![res.message], PdlBlock::Model(trace))) - */ - let mut stream = ollama - .send_chat_messages_with_history_stream( - Arc::new(Mutex::new(history)), - req, - //ollama.generate(GenerationRequest::new(model.into(), prompt), - ) - .await?; - // dbg!("Model result {:?}", &res); - - let mut last_res: Option = None; - let mut response_string = String::new(); - let mut stdout = stdout(); + let mut last_res: Option = None; + let mut response_string = String::new(); + let mut stdout = stdout(); + if emit { stdout.write_all(b"\x1b[1mAssistant: \x1b[0m").await?; - while let Some(Ok(res)) = stream.next().await { + } + while let Some(Ok(res)) = stream.next().await { + if emit { stdout.write_all(b"\x1b[32m").await?; // green stdout.write_all(res.message.content.as_bytes()).await?; stdout.flush().await?; stdout.write_all(b"\x1b[0m").await?; // reset color - response_string += res.message.content.as_str(); - last_res = Some(res); } + response_string += res.message.content.as_str(); + last_res = Some(res); + } + if emit { stdout.write_all(b"\n").await?; + } - let mut trace = block.clone(); - trace.pdl_result = Some(response_string.clone()); - - if let Some(res) = last_res { - if let Some(usage) = res.final_data { - trace.pdl_usage = Some(PdlUsage { - prompt_tokens: usage.prompt_eval_count, - prompt_nanos: usage.prompt_eval_duration, - completion_tokens: usage.eval_count, - completion_nanos: usage.eval_duration, - }); - } - let output_messages = vec![ChatMessage::assistant(response_string)]; - Ok(( - res.message.content.into(), - output_messages, - PdlBlock::Model(trace), - )) - } else { - // nothing came out of the model - Ok(("".into(), vec![], PdlBlock::Model(trace))) - } - // dbg!(history); + (last_res, response_string) + }; + + if let Some(_) = &block.model_response { + if let Some(ref res) = last_res { + self.def( + &block.model_response, + &resultify_as_litellm(&from_str(&to_string(&res)?)?), + &None, + state, + true, + )?; } - _ => Err(Box::from(format!("Unsupported model {}", block.model))), } - } - /// Transform a JSON Value into a PdlResult object - fn resultify(&self, value: &Value) -> PdlResult { - match value { - Value::Null => "".into(), - Value::Bool(b) => b.into(), - Value::Number(n) => n.clone().into(), - Value::String(s) => s.clone().into(), - Value::Array(a) => { - PdlResult::List(a.iter().map(|v| self.resultify(v)).collect::>()) + let usage = if let Some(res) = last_res { + if let Some(usage) = res.final_data { + Some(PdlUsage { + prompt_tokens: usage.prompt_eval_count, + prompt_nanos: Some(usage.prompt_eval_duration), + completion_tokens: usage.eval_count, + completion_nanos: Some(usage.eval_duration), + }) + } else { + None } - Value::Object(m) => PdlResult::Dict( - m.iter() - .map(|(k, v)| (k.clone(), self.resultify(v))) - .collect::>(), - ), + } else { + None + }; + + Ok((response_string, usage)) + } + + /// Run a PdlBlock::Model + async fn run_model( + &mut self, + block: &ModelBlock, + metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + // The input messages to the model is either: + // a) block.input, if given + // b) the current state's accumulated messages + if let Some(c) = &block.context { + state.scope.insert( + "pdl_context".to_string(), + PdlResult::List( + c.iter() + .map(|m| { + PdlResult::Block(PdlBlock::Advanced(Block { + metadata: None, + body: Body::Message(m.clone()), + })) + }) + .collect(), + ), + ); } + + let input_messages = match &block.input { + Some(input) => { + // TODO ignoring result and trace + let (_result, messages, _trace) = self.run_quiet(&*input, state).await?; + messages + } + None => state.messages.clone(), + }; + + let mut trace = block.clone(); + + // TODO, does this belong in run_advanced(), and does + // trace.context belong in Metadata rather than ModelBlock + trace.context = Some( + state + .messages + .iter() + .map(|m| MessageBlock { + role: self.from_ollama_role(&m.role), + content: Box::new(PdlBlock::String(m.content.clone())), + name: None, + tool_call_id: None, + defsite: None, + }) + .collect(), + ); + // TODO, what is the difference between context and pdl_model_input fields? + trace.pdl_model_input = Some( + input_messages + .iter() + .map(|m| MessageBlock { + role: self.from_ollama_role(&m.role), + content: Box::new(PdlBlock::String(m.content.clone())), + name: None, + tool_call_id: None, + defsite: None, + }) + .collect(), + ); + + let (response_string, usage) = + if let PdlResult::String(s) = self.eval_string_to_string(&block.model, state)? { + if s.starts_with("ollama/") || s.starts_with("ollama_chat/") { + self.run_ollama_model(s, block, metadata, state, input_messages) + .await + /*} else if s.starts_with("openai/") { + return self.run_openai_model(s, block, metadata, state, input_messages).await;*/ + } else { + Err(Box::from(format!("Unsupported model {:?}", block.model))) + } + } else { + Err(Box::from(format!( + "Model expression evaluated to non-string {:?}", + block.model + ))) + }?; + + trace.pdl_usage = usage; + + Ok(( + PdlResult::String(response_string.clone()), + vec![ChatMessage::assistant(response_string)], + Model(trace), + )) } /// Run a PdlBlock::Data - async fn run_data(&mut self, block: &DataBlock, _context: Context) -> Interpretation { - if self.debug { + async fn run_data( + &mut self, + block: &DataBlock, + metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { eprintln!("Data raw={:?} {:?}", block.raw, block.data); } let mut trace = block.clone(); if let Some(true) = block.raw { - let result = self.def(&block.def, &self.resultify(&block.data), &block.parser)?; - Ok((result, vec![], PdlBlock::Data(trace))) + let result = self.def( + &metadata.def, + &resultify(&block.data), + &block.parser, + state, + true, + )?; + Ok((result, vec![], Data(trace))) } else { - let result = self.def(&block.def, &self.eval_json(&block.data)?, &block.parser)?; + let result = self.def( + &metadata.def, + &self.eval_json(&block.data, state)?, + &block.parser, + state, + true, + )?; trace.data = from_str(to_string(&result)?.as_str())?; - Ok((result, vec![], PdlBlock::Data(trace))) + let messages = match &trace.data { + Value::Object(m) => match m.get("pdl__result") { + Some(Value::Array(a)) => a + .iter() + .filter_map(|m| match m { + Value::Object(d) => match d.get("content") { + Some(Value::String(s)) => Some(ChatMessage::user(s.clone())), + _ => None, + }, + _ => None, + }) + .collect(), + _ => vec![], + }, + _ => vec![], + }; + Ok((result, messages, Data(trace))) } } - async fn run_object(&mut self, block: &ObjectBlock, context: Context) -> Interpretation { - if self.debug { + async fn run_object( + &mut self, + block: &ObjectBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { eprintln!("Object {:?}", block.object); } @@ -671,8 +1107,7 @@ impl<'a> Interpreter<'a> { let mut iter = block.object.iter(); while let Some((k, v)) = iter.next() { - let (this_result, this_messages, this_trace) = - self.run_quiet(v, context.clone()).await?; + let (this_result, this_messages, this_trace) = self.run_quiet(v, state).await?; messages.extend(this_messages); result_map.insert(k.clone(), this_result); trace_map.insert(k.clone(), this_trace); @@ -681,52 +1116,57 @@ impl<'a> Interpreter<'a> { Ok(( PdlResult::Dict(result_map), messages, - PdlBlock::Object(ObjectBlock { object: trace_map }), + Object(ObjectBlock { object: trace_map }), )) } /// Run a PdlBlock::Repeat - async fn run_repeat(&mut self, block: &RepeatBlock, context: Context) -> Interpretation { - // { i:[1,2,3], j: [4,5,6]} -> ([i,j], [[1,2,3],[4,5,6]]) - // let (variables, values): (Vec<_>, Vec>) = block - // .into_iter() - // .unzip(); + async fn run_repeat( + &mut self, + block: &RepeatBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { let iter_scopes = block .for_ .iter() - .map(|(var, values)| match self.eval_list_or_string(values) { - Ok(value) => Ok((var.clone(), value)), - Err(e) => Err(e), - }) + .map( + |(var, values)| match self.eval_list_or_string(values, state) { + Ok(value) => Ok((var.clone(), value)), + Err(e) => Err(e), + }, + ) .collect::, _>>()?; - if self.debug { + if self.options.debug { eprintln!("Repeat {:?}", iter_scopes); } let mut results = vec![]; let mut messages = vec![]; let mut trace = vec![]; + let mut iter_state = state.with_iter(0); if let Some(n) = iter_scopes.iter().map(|(_, v)| v.len()).min() { for iter in 0..n { let this_iter_scope = iter_scopes .iter() .map(|(k, v)| (k.clone(), v[iter].clone())) .collect(); - self.push_and_extend_scope(this_iter_scope); - let (result, ms, t) = self.run_quiet(&block.repeat, context.clone()).await?; + iter_state = iter_state + .incr_iter(iter) + .extend_scope(vec![this_iter_scope]); + let (result, ms, t) = self.run_quiet(&block.repeat, &mut iter_state).await?; results.push(result); messages.extend(ms); trace.push(t); - self.pop_scope(); + //self.pop_scope(); } } - Ok(( - PdlResult::List(results), - messages, - PdlBlock::Repeat(block.clone()), - )) + state.scope = iter_state.scope; + state.escaped_variables = iter_state.escaped_variables; + + Ok((PdlResult::List(results), messages, Repeat(block.clone()))) } fn to_ollama_role(&self, role: &Role) -> MessageRole { @@ -738,6 +1178,15 @@ impl<'a> Interpreter<'a> { } } + fn from_ollama_role(&self, role: &MessageRole) -> Role { + match role { + MessageRole::User => Role::User, + MessageRole::Assistant => Role::Assistant, + MessageRole::System => Role::System, + MessageRole::Tool => Role::Tool, + } + } + fn parse_result(&self, parser: &PdlParser, result: &String) -> Result { match parser { PdlParser::Json => from_str(result).map_err(|e| Box::from(e)), @@ -745,40 +1194,20 @@ impl<'a> Interpreter<'a> { } } - fn push_and_extend_scope(&mut self, scope: HashMap) { - let mut new_scope = self.scope.last().unwrap_or(&HashMap::new()).clone(); - new_scope.extend(scope); - self.scope.push(new_scope); - } - - fn push_and_extend_scope_with( - &mut self, - mut scope: HashMap, - other_scope: HashMap, - ) { - scope.extend(other_scope); - self.push_and_extend_scope(scope); - } - - fn pop_scope(&mut self) { - self.scope.pop(); - } - async fn process_defs( &mut self, defs: &Option>, + state: &mut State, ) -> Result<(), PdlError> { - let mut new_scope: Scope = HashMap::new(); - if let Some(cur_scope) = self.scope.last() { - new_scope.extend(cur_scope.clone()); - } - self.scope.push(new_scope); + // in pdl, blocks are not a lexical scope. strange, but true + //let mut new_state = state.clone(); if let Some(defs) = defs { let mut iter = defs.iter(); while let Some((var, def)) = iter.next() { - let (result, _, _) = self.run_quiet(def, vec![]).await?; - let _ = self.def(&Some(var.clone()), &result, &None); + let (result, _, _) = self.run_quiet(def, state).await?; + let escape = false; // ?? do we want pdl defs to escape to the parent scope? lexical scoping would say no + let _ = self.def(&Some(var.clone()), &result, &None, state, escape); } } @@ -789,60 +1218,72 @@ impl<'a> Interpreter<'a> { async fn run_sequence( &mut self, block: &impl SequencingBlock, - context: Context, - ) -> Interpretation { - if self.debug { - let description = if let Some(d) = block.description() { - d - } else { - &"".to_string() - }; - eprintln!("{} {description}", block.kind()); + metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { + if self.options.debug { + eprintln!( + "{} {:?}", + block.kind(), + metadata.description.clone().unwrap_or_default() + ); } - let mut input_messages = context.clone(); let mut output_results = vec![]; let mut output_messages = vec![]; let mut output_blocks = vec![]; - self.process_defs(block.defs()).await?; - + // here is where we iterate over the sequence items let mut iter = block.items().iter(); + let mut idx = 0; + let mut iter_state = state.with_iter(idx); while let Some(block) = iter.next() { + idx += 1; + // run each element of the Text block - let (this_result, this_messages, trace) = - self.run_quiet(&block, input_messages.clone()).await?; - input_messages.extend(this_messages.clone()); - output_results.push(this_result); + let (this_result, this_messages, trace) = self.run(&block, &mut iter_state).await?; + + iter_state = iter_state.incr_iter(idx); + iter_state.messages.extend(this_messages.iter().cloned()); - output_messages.extend(this_messages); + output_results.push(this_result); + output_messages.extend(this_messages.iter().cloned()); output_blocks.push(trace); } - self.scope.pop(); - let trace = block.with_items(output_blocks); let result = self.def( - trace.def(), + &metadata.def, &trace.result_for(output_results), trace.parser(), + &mut iter_state, + true, )?; - let result_messages = trace.messages_for::(output_messages); - Ok(( - result, - match block.role() { - Some(role) => result_messages - .into_iter() - .map(|m| ChatMessage::new(self.to_ollama_role(role), m.content)) - .collect(), - None => result_messages, - }, - trace.to_block(), - )) + + state.scope = iter_state.scope; + state.escaped_variables = iter_state.escaped_variables; + + // We may be asked to overlay a role on to the messages (TODO, + // does this belong in common code, i.e. run_advanced()?) + let result_messages = trace.messages_for::(&output_messages); + let messages = match block.role() { + Some(role) => result_messages + .into_iter() + .map(|m| ChatMessage::new(self.to_ollama_role(role), m.content)) + .collect(), + None => result_messages, + }; + + Ok((result, messages, trace.to_block())) } /// Run a PdlBlock::Array - async fn run_array(&mut self, block: &ArrayBlock, context: Context) -> Interpretation { + async fn run_array( + &mut self, + block: &ArrayBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { let mut result_items = vec![]; let mut all_messages = vec![]; let mut trace_items = vec![]; @@ -850,30 +1291,34 @@ impl<'a> Interpreter<'a> { let mut iter = block.array.iter(); while let Some(item) = iter.next() { // TODO accumulate messages - let (result, messages, trace) = self.run_quiet(item, context.clone()).await?; + let (result, messages, trace) = self.run_quiet(item, state).await?; result_items.push(result); all_messages.extend(messages); trace_items.push(trace); } - Ok(( - PdlResult::List(result_items), - all_messages, - PdlBlock::Array(ArrayBlock { array: trace_items }), - )) + let mut trace = block.clone(); + trace.array = trace_items; + + Ok((PdlResult::List(result_items), all_messages, Array(trace))) } /// Run a PdlBlock::Message - async fn run_message(&mut self, block: &MessageBlock, context: Context) -> Interpretation { + async fn run_message( + &mut self, + block: &MessageBlock, + _metadata: &Metadata, + state: &mut State, + ) -> BodyInterpretation { let (content_result, content_messages, content_trace) = - self.run(&block.content, context).await?; + self.run(&block.content, state).await?; let name = if let Some(name) = &block.name { - Some(self.eval_to_string(&name)?) + Some(self.eval_to_string(&name, state)?) } else { None }; let tool_call_id = if let Some(tool_call_id) = &block.tool_call_id { - Some(self.eval_to_string(&tool_call_id)?) + Some(self.eval_to_string(&tool_call_id, state)?) } else { None }; @@ -897,60 +1342,103 @@ impl<'a> Interpreter<'a> { .into_iter() .map(|m| ChatMessage::new(self.to_ollama_role(&block.role), m.content)) .collect(), - PdlBlock::Message(MessageBlock { + Message(MessageBlock { role: block.role.clone(), content: Box::new(content_trace), - description: block.description.clone(), name: name, + defsite: None, tool_call_id: tool_call_id, }), )) } } -pub async fn run(program: &PdlBlock, cwd: Option, debug: bool) -> Interpretation { - let mut interpreter = Interpreter::new(); - interpreter.debug = debug; +pub async fn run<'a>( + program: &PdlBlock, + cwd: Option, + options: RunOptions<'a>, + initial_scope: Scope, +) -> Interpretation { + crate::pdl::pull::pull_if_needed(&program).await?; + let trace_file = options.trace.clone(); + let mut interpreter = Interpreter::new(options); + let mut state = State::new(initial_scope); if let Some(cwd) = cwd { - interpreter.cwd = cwd - }; - interpreter.run(&program, vec![]).await + state.cwd = cwd + } + + let res = interpreter.run(&program, &mut state).await?; + if let Some(trace_file) = trace_file { + let file = ::std::fs::File::create(trace_file)?; + let mut writer = ::std::io::BufWriter::new(file); + serde_json::to_writer(&mut writer, &res.2)?; + } + + Ok(res) } -pub fn run_sync(program: &PdlBlock, cwd: Option, debug: bool) -> InterpretationSync { - tauri::async_runtime::block_on(run(program, cwd, debug)) +#[allow(dead_code)] +pub fn run_sync<'a>( + program: &PdlBlock, + cwd: Option, + options: RunOptions<'a>, + initial_scope: Scope, +) -> InterpretationSync { + tauri::async_runtime::block_on(run(program, cwd, options, initial_scope)) .map_err(|err| Box::::from(err.to_string())) } /// Read in a file from disk and parse it as a PDL program -fn parse_file(path: &PathBuf) -> Result { - from_reader(File::open(path)?) - .map_err(|err| Box::::from(err.to_string())) +pub fn parse_file(path: &PathBuf) -> Result { + from_reader(::std::fs::File::open(path)?).map_err(|err| PdlError::from(err.to_string())) } -pub async fn run_file(source_file_path: &str, debug: bool) -> Interpretation { +pub async fn run_file<'a>( + source_file_path: &str, + options: RunOptions<'a>, + initial_scope: Scope, +) -> Interpretation { let path = PathBuf::from(source_file_path); let cwd = path.parent().and_then(|cwd| Some(cwd.to_path_buf())); let program = parse_file(&path)?; + // println!("{}", serde_json::to_string_pretty(&program)?); - run(&program, cwd, debug).await + run(&program, cwd, options, initial_scope).await } -pub fn run_file_sync(source_file_path: &str, debug: bool) -> InterpretationSync { - tauri::async_runtime::block_on(run_file(source_file_path, debug)) +pub fn run_file_sync<'a>( + source_file_path: &str, + options: RunOptions<'a>, + initial_scope: Scope, +) -> InterpretationSync { + tauri::async_runtime::block_on(run_file(source_file_path, options, initial_scope)) .map_err(|err| Box::::from(err.to_string())) } -pub async fn run_string(source: &str, debug: bool) -> Interpretation { - run(&from_yaml_str(source)?, None, debug).await +pub async fn run_string<'a>( + source: &str, + options: RunOptions<'a>, + initial_scope: Scope, +) -> Interpretation { + run(&from_yaml_str(source)?, None, options, initial_scope).await } -pub async fn run_json(source: Value, debug: bool) -> Interpretation { - run_string(&to_string(&source)?, debug).await +#[allow(dead_code)] +pub async fn run_json<'a>( + source: Value, + options: RunOptions<'a>, + initial_scope: Scope, +) -> Interpretation { + run_string(&to_string(&source)?, options, initial_scope).await } -pub fn run_json_sync(source: Value, debug: bool) -> InterpretationSync { - tauri::async_runtime::block_on(run_json(source, debug)) +#[allow(dead_code)] +pub fn run_json_sync<'a>( + source: Value, + options: RunOptions<'a>, + initial_scope: Scope, +) -> InterpretationSync { + tauri::async_runtime::block_on(run_json(source, options, initial_scope)) .map_err(|err| Box::::from(err.to_string())) } @@ -978,3 +1466,91 @@ pub fn pretty_print(messages: &Vec) -> String { .collect::>() .join("\n") } + +/// Transform a JSON Value into a PdlResult object +fn resultify(value: &Value) -> PdlResult { + match value { + Value::Null => "".into(), + Value::Bool(b) => b.into(), + Value::Number(n) => n.clone().into(), + Value::String(s) => s.clone().into(), + Value::Array(a) => PdlResult::List(a.iter().map(|v| resultify(v)).collect::>()), + Value::Object(m) => PdlResult::Dict( + m.iter() + .map(|(k, v)| (k.clone(), resultify(v))) + .collect::>(), + ), + } +} + +/// Transform a JSON Value into a PdlResult object that is compatible with litellm's model response schema +fn resultify_as_litellm(value: &Value) -> PdlResult { + resultify(&json!({ + "choices": [ + value + ] + })) +} + +pub fn load_scope( + data: Option<&str>, + data_file: Option<&str>, + value: Option, +) -> Result> { + let mut scope = HashMap::new(); + + if let Some(data_file) = data_file { + if let PdlResult::Dict(d) = resultify(&from_reader(::std::fs::File::open(data_file)?)?) { + scope.extend(d); + } else { + return Err(Box::from(format!( + "Data file {data_file} does not contain a dictionary" + ))); + } + } + + if let Some(data) = data { + if let PdlResult::Dict(d) = resultify(&from_yaml_str(data)?) { + scope.extend(d); + } else { + return Err(Box::from(format!("Data is not a dictionary"))); + } + } + + if let Some(value) = value { + if let PdlResult::Dict(d) = resultify(&value) { + scope.extend(d); + } else { + return Err(Box::from(format!("Data is not a dictionary"))); + } + } + + Ok(scope) +} + +/// Helper function to retrieve a sequence of paths from an environment variable. +fn get_paths(env_variable_name: &str) -> impl Iterator + '_ { + ::std::env::var_os(env_variable_name) + .into_iter() + .flat_map(move |paths| { + split_paths(&paths) + .map(|path| { + path.into_os_string() + .into_string() + .unwrap_or_else(|_| panic!("{env_variable_name} isn't valid unicode")) + }) + .collect::>() + }) +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) use ::std::env::split_paths; +#[cfg(target_os = "wasi")] +pub(crate) fn split_paths + ?Sized>( + s: &T, +) -> impl Iterator + '_ { + use std::os::wasi::ffi::OsStrExt; + let s = s.as_ref().as_bytes(); + s.split(|b| *b == b':') + .map(|x| std::ffi::OsStr::from_bytes(x).to_owned().into()) +} diff --git a/pdl-live-react/src-tauri/src/pdl/interpreter_tests.rs b/pdl-live-react/src-tauri/src/pdl/interpreter_tests.rs index 7d9f3c278..187bf1690 100644 --- a/pdl-live-react/src-tauri/src/pdl/interpreter_tests.rs +++ b/pdl-live-react/src-tauri/src/pdl/interpreter_tests.rs @@ -1,33 +1,77 @@ #[cfg(test)] mod tests { - // use super::*; use ::std::error::Error; use serde_json::json; use crate::pdl::{ - ast::{ModelBlock, PdlBlock}, - interpreter::{run_json_sync as run_json, run_sync as run}, + ast::{Block, Body::*, ModelBlockBuilder, PdlBlock, PdlBlock::Advanced, Scope}, + interpreter::{RunOptions, load_scope, run_json_sync as run_json, run_sync as run}, }; use ollama_rs::generation::chat::MessageRole; const DEFAULT_MODEL: &'static str = "ollama/granite3.2:2b"; + fn streaming<'a>() -> RunOptions<'a> { + let mut o: RunOptions = Default::default(); + o.stream = true; + o + } + + fn non_streaming<'a>() -> RunOptions<'a> { + let mut o: RunOptions = Default::default(); + o.stream = false; + o + } + + fn initial_scope() -> Scope { + Default::default() + } + #[test] fn string() -> Result<(), Box> { - let (_, messages, _) = run(&"hello".into(), None, false)?; + let (_, messages, _) = run(&"hello".into(), None, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "hello"); Ok(()) } + #[test] + fn string_via_initial_scope() -> Result<(), Box> { + let (_, messages, _) = run( + &"${x}".into(), + None, + streaming(), + load_scope( + None, + None, + Some(json!({ + "x": 333 + })), + )?, + )?; + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].role, MessageRole::User); + assert_eq!(messages[0].content, "333"); + Ok(()) + } + #[test] fn single_model_via_input_string() -> Result<(), Box> { let (_, messages, _) = run( - &PdlBlock::Model(ModelBlock::new(DEFAULT_MODEL).input_str("hello").build()), + &Advanced(Block { + metadata: None, + body: Model( + ModelBlockBuilder::default() + .model(DEFAULT_MODEL) + .input(Box::from(PdlBlock::String("hello".to_string()))) + .build()?, + ), + }), None, - false, + streaming(), + initial_scope(), )?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::Assistant); @@ -35,6 +79,26 @@ mod tests { Ok(()) } + #[test] + fn single_model_via_text_chain_expr() -> Result<(), Box> { + let (_, messages, _) = run_json( + json!({ + "text": [ + "hello", + {"model": { "pdl__expr": DEFAULT_MODEL }} + ] + }), + streaming(), + initial_scope(), + )?; + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].role, MessageRole::User); + assert_eq!(messages[0].content, "hello"); + assert_eq!(messages[1].role, MessageRole::Assistant); + assert!(messages[1].content.contains("Hello!")); + Ok(()) + } + #[test] fn single_model_via_text_chain() -> Result<(), Box> { let (_, messages, _) = run_json( @@ -44,7 +108,8 @@ mod tests { { "model": DEFAULT_MODEL } ] }), - false, + streaming(), + initial_scope(), )?; assert_eq!(messages.len(), 2); assert_eq!(messages[0].role, MessageRole::User); @@ -66,7 +131,8 @@ mod tests { ] } }), - false, + streaming(), + initial_scope(), )?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::Assistant); @@ -91,7 +157,8 @@ mod tests { { "model": DEFAULT_MODEL }, ] }), - false, + streaming(), + initial_scope(), )?; assert_eq!(messages.len(), 4); assert_eq!(messages[0].role, MessageRole::User); @@ -129,7 +196,7 @@ mod tests { ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 2); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, json); @@ -148,7 +215,7 @@ mod tests { ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "value"); @@ -174,7 +241,7 @@ mod tests { ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "hello world"); @@ -202,7 +269,7 @@ mod tests { ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "hello world 4"); @@ -216,7 +283,7 @@ mod tests { "code":"print('hi ho'); result = 33" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "33"); @@ -230,7 +297,7 @@ mod tests { "code":"print('hi ho'); result = 'foo'" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "foo"); @@ -244,13 +311,52 @@ mod tests { "code":"print('hi ho'); result = {\"foo\": 3}" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "{'foo': 3}"); Ok(()) } + #[test] + fn text_python_two_code_result_dict() -> Result<(), Box> { + let program = json!({ + "text": [ + { "lang": "python", + "code":"print('hi ho'); result = {\"foo\": 3}" + }, + { "lang": "python", + "code":"import os; print('hi ho'); result = {\"foo\": 4}" + } + ] + }); + + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].role, MessageRole::User); + assert_eq!(messages[0].content, "{'foo': 3}"); + assert_eq!(messages[1].role, MessageRole::User); + assert_eq!(messages[1].content, "{'foo': 4}"); + Ok(()) + } + + // TODO: illegal instruction, but only during tests + #[test] + fn text_python_code_import_venv() -> Result<(), Box> { + let program = json!({ + "include": "./tests/cli/code-python.pdl" + }); + + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].role, MessageRole::User); + assert_eq!( + messages[0].content, + "{'foo': None, 'diff': {'D': {'two': {'N': 3}}}}" + ); + Ok(()) + } + #[test] fn text_read_file_text() -> Result<(), Box> { let program = json!({ @@ -258,7 +364,7 @@ mod tests { "read":"./tests/data/foo.txt" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "this should be foo\n"); @@ -274,7 +380,7 @@ mod tests { ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 2); assert_eq!(messages[0].role, MessageRole::User); assert_eq!( @@ -301,7 +407,7 @@ mod tests { } }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 3); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "2"); @@ -326,7 +432,7 @@ mod tests { } }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 3); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "5"); @@ -351,7 +457,7 @@ mod tests { } }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 3); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "4a"); @@ -369,7 +475,7 @@ mod tests { "then": "good" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "good"); @@ -384,7 +490,7 @@ mod tests { "else": "good" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "good"); @@ -402,7 +508,7 @@ mod tests { "else": "good" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "good"); @@ -424,7 +530,7 @@ mod tests { "text": [ "${ obj.a }" ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "good on object"); @@ -450,7 +556,7 @@ mod tests { "text": [ "${ obj.a.b }" ] }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "good on object"); @@ -463,7 +569,7 @@ mod tests { "include": "./tests/cli/call-with-args.pdl" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "hello world 4 bye"); @@ -476,7 +582,7 @@ mod tests { "include": "./tests/cli/data1.pdl" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "xxxx3true"); @@ -489,7 +595,7 @@ mod tests { "include": "./tests/cli/data2.pdl" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "xxxx3true"); @@ -502,7 +608,7 @@ mod tests { "include": "./tests/cli/data3.pdl" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "${x}3true"); @@ -515,7 +621,7 @@ mod tests { "include": "./tests/cli/data4.pdl" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "yyyyxxxx3true"); @@ -528,10 +634,50 @@ mod tests { "include": "../../examples/tutorial/import.pdl" }); - let (_, messages, _) = run_json(program, false)?; + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, MessageRole::User); assert_eq!(messages[0].content, "Bye!"); Ok(()) } + + #[test] + fn scoping_1() -> Result<(), Box> { + let program = json!({ + "include": "./tests/cli/scoping_1.pdl" + }); + + let (_, messages, _) = run_json(program, streaming(), initial_scope())?; + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].role, MessageRole::User); + assert_eq!(messages[0].content, "3yo"); + assert_eq!(messages[1].role, MessageRole::User); + assert_eq!(messages[1].content, "mo"); + Ok(()) + } + + #[test] + fn bee_1() -> Result<(), Box> { + let program = crate::compile::beeai::compile("./tests/data/bee_1.py", false)?; + let (_, messages, _) = run(&program, None, non_streaming(), initial_scope())?; + assert_eq!(messages.len(), 9); + assert!( + messages.iter().any(|m| m.role == MessageRole::User + && m.content == "Provide a short history of Saint-Tropez."), + "Could not find message user:Provide a short history of Saint-Tropez. in {:?}", + messages + ); + assert!( + messages.iter().any(|m| m.role == MessageRole::System + && m.content + == "You can combine disparate information into a final coherent summary."), + "Could not find message system:Provide a short history of Saint-Tropez. in {:?}", + messages + ); + // assert!(messages.iter().any(|m| m.role == MessageRole::Assistant && m.content.contains("a renowned French Riviera town")), "Could not find message assistant:a renowned French Riviera town in {:?}", messages); + //assert_eq!(true, messages.iter().any(|m| m.role == MessageRole::Assistant && m.content.contains("I'll use the OpenMeteoTool"))); + //assert_eq!(true, messages.iter().any(|m| m.role == MessageRole::Assistant && m.content.contains("The current temperature in Saint-Tropez"))); + + Ok(()) + } } diff --git a/pdl-live-react/src-tauri/src/pdl/mod.rs b/pdl-live-react/src-tauri/src/pdl/mod.rs index fcdcab28b..179cb9e10 100644 --- a/pdl-live-react/src-tauri/src/pdl/mod.rs +++ b/pdl-live-react/src-tauri/src/pdl/mod.rs @@ -5,4 +5,3 @@ mod interpreter_tests; pub mod pip; pub mod pull; pub mod requirements; -pub mod run; diff --git a/pdl-live-react/src-tauri/src/pdl/pull.rs b/pdl-live-react/src-tauri/src/pdl/pull.rs index 6f4302b8d..7b11e3113 100644 --- a/pdl-live-react/src-tauri/src/pdl/pull.rs +++ b/pdl-live-react/src-tauri/src/pdl/pull.rs @@ -1,20 +1,24 @@ -use ::std::io::{Error, ErrorKind}; +use ::std::io::Error; use duct::cmd; +use fs4::fs_std::FileExt; use rayon::prelude::*; -use yaml_rust2::{Yaml, YamlLoader}; +use crate::pdl::ast::PdlBlock; use crate::pdl::extract; -/// Read the given filesystem path and produce a potentially multi-document Yaml -fn from_path(path: &str) -> Result, Error> { - let content = std::fs::read_to_string(path)?; - YamlLoader::load_from_str(&content).map_err(|e| Error::new(ErrorKind::Other, e.to_string())) -} +/* pub async fn pull_if_needed_from_path( + source_file_path: &str, +) -> Result<(), Box> { + let program = parse_file(&::std::path::PathBuf::from(source_file_path))?; + pull_if_needed(&program) + .await + .map_err(|e| Box::from(e.to_string())) +} */ /// Pull models (in parallel) from the PDL program in the given filepath. -pub async fn pull_if_needed(path: &str) -> Result<(), Error> { - extract::extract_models(from_path(path)?) +pub async fn pull_if_needed(program: &PdlBlock) -> Result<(), Error> { + extract::extract_models(program) .into_par_iter() .try_for_each(|model| match model { m if model.starts_with("ollama/") => ollama_pull_if_needed(&m[7..]), @@ -42,8 +46,20 @@ fn ollama_exists(model: &str) -> bool { /// The Ollama implementation of a single model pull fn ollama_pull_if_needed(model: &str) -> Result<(), Error> { - if !ollama_exists(model) { - cmd!("ollama", "pull", model).stdout_to_stderr().run()?; - } - Ok(()) + let path = ::std::env::temp_dir().join(format!("pdl-ollama-pull-{model}")); + let f = ::std::fs::File::create(path)?; + f.lock_exclusive()?; + + // don't ? the cmd! so that we can "finally" unlock the file + let res = if !ollama_exists(model) { + cmd!("ollama", "pull", model) + .stdout_to_stderr() + .run() + .and_then(|_| Ok(())) + } else { + Ok(()) + }; + + FileExt::unlock(&f)?; + res } diff --git a/pdl-live-react/src-tauri/src/pdl/requirements.rs b/pdl-live-react/src-tauri/src/pdl/requirements.rs index 2b08c9f16..6e6bb0501 100644 --- a/pdl-live-react/src-tauri/src/pdl/requirements.rs +++ b/pdl-live-react/src-tauri/src/pdl/requirements.rs @@ -1,5 +1,2 @@ -//pub const PDL_INTERPRETER: &str = "-e ../"; -pub const PDL_INTERPRETER: &str = "prompt-declaration-language==0.6.0"; - pub const BEEAI_FRAMEWORK: &str = "-e git+https://github.com/starpit/bee-agent-framework.git@nick-meta-combo#egg=beeai_framework&subdirectory=python"; //pub const BEEAI_FRAMEWORK: &str = "beeai_framework==0.1"; diff --git a/pdl-live-react/src-tauri/src/pdl/run.rs b/pdl-live-react/src-tauri/src/pdl/run.rs deleted file mode 100644 index 2bc6c18dd..000000000 --- a/pdl-live-react/src-tauri/src/pdl/run.rs +++ /dev/null @@ -1,50 +0,0 @@ -use ::std::path::Path; -use duct::cmd; -use futures::executor::block_on; - -use crate::pdl::pip::pip_install_if_needed; -use crate::pdl::pull::pull_if_needed; -use crate::pdl::requirements::PDL_INTERPRETER; - -#[cfg(desktop)] -pub fn run_pdl_program( - source_file_path: &str, - trace_file: Option<&str>, - data: Option<&str>, - stream: Option<&str>, -) -> Result<(), Box> { - println!( - "Running {:#?}", - Path::new(&source_file_path).file_name().unwrap() - ); - - // async the model pull and pip installs - let pull_future = pull_if_needed(&source_file_path); - let bin_path_future = pip_install_if_needed(&PDL_INTERPRETER); - - // wait for any model pulls to finish - block_on(pull_future)?; - - // wait for any pip installs to finish - let bin_path = block_on(bin_path_future)?; - - let mut args = vec![ - source_file_path.to_string(), - dashdash("--trace", trace_file), - dashdash("--data", data), - dashdash("--stream", stream), - ]; - args.retain(|x| x.chars().count() > 0); - cmd(bin_path.join("pdl"), &args).run()?; - - Ok(()) -} - -/// Format `--{opt}={a}` based on whether `a` is given or not -fn dashdash(opt: &str, a: Option<&str>) -> String { - if let Some(s) = a { - format!("{}={}", opt, s) - } else { - "".to_owned() - } -} diff --git a/pdl-live-react/src-tauri/tauri.conf.json b/pdl-live-react/src-tauri/tauri.conf.json index e35d1fd32..7f8f9a864 100644 --- a/pdl-live-react/src-tauri/tauri.conf.json +++ b/pdl-live-react/src-tauri/tauri.conf.json @@ -47,20 +47,6 @@ } } }, - "runr": { - "args": [ - { - "name": "source", - "index": 1, - "required": true, - "takesValue": true - }, - { - "name": "debug", - "short": "g" - } - ] - }, "run": { "description": "Run a PDL program", "args": [ @@ -71,16 +57,25 @@ "takesValue": true }, { - "name": "stream", - "takesValue": true + "name": "no-stream" }, { "name": "data", + "short": "d", + "takesValue": true + }, + { + "name": "data-file", + "short": "f", "takesValue": true }, { "name": "trace", "takesValue": true + }, + { + "name": "debug", + "short": "g" } ] }, diff --git a/pdl-live-react/src-tauri/tests/cli/code-python.pdl b/pdl-live-react/src-tauri/tests/cli/code-python.pdl index 674e60aff..70f203120 100644 --- a/pdl-live-react/src-tauri/tests/cli/code-python.pdl +++ b/pdl-live-react/src-tauri/tests/cli/code-python.pdl @@ -1,2 +1,10 @@ lang: python -code: 'print(''hi ho''); result = {"foo": 3}' +code: | + import sys # test import stdlib + import os # test import stdlib + print(f"!!! {sys.path}") + #import textdistance # test import from venv + from nested_diff import diff # test import from venv + a = {'one': 1, 'two': 2, 'three': 3} + b = {'one': 1, 'two': 3, 'three': 3} + result = {"foo": os.getenv("FOO999999999999999999999999"), "diff": diff(a, b, O=False, U=False)} diff --git a/pdl-live-react/src-tauri/tests/cli/model-input-array.pdl b/pdl-live-react/src-tauri/tests/cli/model-input-array.pdl index 7fbce7342..18c18124a 100644 --- a/pdl-live-react/src-tauri/tests/cli/model-input-array.pdl +++ b/pdl-live-react/src-tauri/tests/cli/model-input-array.pdl @@ -1,7 +1,7 @@ model: ollama/granite3.2:2b input: array: - - role: system + - role: user content: answer as if you live in europe - role: user content: what is the fastest animal where i live? diff --git a/pdl-live-react/src-tauri/tests/cli/model-input-nested.pdl b/pdl-live-react/src-tauri/tests/cli/model-input-nested.pdl new file mode 100644 index 000000000..096b698d4 --- /dev/null +++ b/pdl-live-react/src-tauri/tests/cli/model-input-nested.pdl @@ -0,0 +1,7 @@ +text: + - role: user + content: answer as if you live in europe + - text: + - role: user + content: what is the fastest animal where i live? + - model: ollama/granite3.2:2b diff --git a/pdl-live-react/src-tauri/tests/cli/scoping_1.pdl b/pdl-live-react/src-tauri/tests/cli/scoping_1.pdl new file mode 100644 index 000000000..12c773d90 --- /dev/null +++ b/pdl-live-react/src-tauri/tests/cli/scoping_1.pdl @@ -0,0 +1,6 @@ +text: + - defs: + x: 3 + text: + - ${x~"yo"} + - ${x~"mo"} diff --git a/pdl-live-react/src-tauri/tests/cli/scoping_1_wrapper.pdl b/pdl-live-react/src-tauri/tests/cli/scoping_1_wrapper.pdl new file mode 100644 index 000000000..7adaa0590 --- /dev/null +++ b/pdl-live-react/src-tauri/tests/cli/scoping_1_wrapper.pdl @@ -0,0 +1 @@ +include: ./scoping_1.pdl diff --git a/pdl-live-react/src-tauri/tests/data/bee_1.py b/pdl-live-react/src-tauri/tests/data/bee_1.py new file mode 100644 index 000000000..85fabf131 --- /dev/null +++ b/pdl-live-react/src-tauri/tests/data/bee_1.py @@ -0,0 +1,64 @@ +import asyncio + +from beeai_framework.backend.chat import ChatModel +from beeai_framework.tools.search.wikipedia import WikipediaTool +from beeai_framework.tools.weather.openmeteo import OpenMeteoTool +from beeai_framework.workflows.agent import AgentWorkflow, AgentWorkflowInput + + +async def main() -> None: + llm = ChatModel.from_name("ollama:granite3.2:2b") + workflow = AgentWorkflow(name="Smart assistant") + + workflow.add_agent( + name="Researcher", + role="A diligent researcher.", + instructions="You look up and provide information about a specific topic.", + tools=[WikipediaTool()], + llm=llm, + ) + + workflow.add_agent( + name="WeatherForecaster", + role="A weather reporter.", + instructions="You provide detailed weather reports.", + tools=[OpenMeteoTool()], + llm=llm, + ) + + workflow.add_agent( + name="DataSynthesizer", + role="A meticulous and creative data synthesizer", + instructions="You can combine disparate information into a final coherent summary.", + llm=llm, + ) + + location = "Saint-Tropez" + + response = await workflow.run( + inputs=[ + AgentWorkflowInput( + prompt=f"Provide a short history of {location}.", + ), + AgentWorkflowInput( + prompt=f"Provide a comprehensive weather summary for {location} today.", + expected_output="Essential weather details such as chance of rain, temperature and wind. Only report information that is available.", + ), + AgentWorkflowInput( + prompt=f"Summarize the historical and weather data for {location}.", + expected_output=f"A paragraph that describes the history of {location}, followed by the current weather conditions.", + ), + ] + ).on( + "success", + lambda data, event: print( + f"\n-> Step '{data.step}' has been completed with the following outcome.\n\n{data.state.final_answer}" + ), + ) + + print("==== Final Answer ====") + print(response.result.final_answer) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index 5d834f936..8ffe50962 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,12 +12,12 @@ dependencies = [ "jinja2~=3.0", "PyYAML~=6.0", "jsonschema~=4.0", - "litellm>=1.57.3,!=1.59.9,!=1.63.14", - "openai==1.61.0", + "litellm>=1.66.0", + "openai>=1.66.1", "termcolor~=2.0", "ipython>=8,<10", "json-repair~=0.35", - "granite-io~=0.2", + "granite-io>=0.2,<0.4", ] authors = [ { name="Mandana Vaziri", email="mvaziri@us.ibm.com" }, diff --git a/tests/results/examples/demo/9-react.0.result b/tests/results/examples/demo/9-react.0.result index 553886bd1..679bcf2cd 100644 --- a/tests/results/examples/demo/9-react.0.result +++ b/tests/results/examples/demo/9-react.0.result @@ -1,16 +1,14 @@ -How many years ago was the discoverer of the Hudson River born? Keep in mind we are in 2025. -Thought: I need to find out who discovered the Hudson River and then calculate how many years ago they were born, given that it's currently 2025. +How many years ago was the discoverer of the Hudson River born? Keep in mind we are in 2025. When searching for a birthday for a person, simply ask for the name of that person. +Thought: I need to find out who discovered the Hudson River and then calculate how many years ago their birthdate was. Action: -[{"name": "Search", "arguments": {"topic": "Hudson River discoverer"}}] -Observation: The Hudson River was discovered by Henry Hudson in 1609. -Thought: Henry Hudson was born around 1565 to 1570. I need to find the exact birth year and then calculate how many years ago it was from 2025. +[{"name": "Search", "arguments": {"topic": "Henry Hudson"}}] +Observation: Henry Hudson (c. 1565 – disappeared 23 June 1611) was an English sea explorer and navigator during the early 17th century, best known for his explorations of present-day Canada and parts of the Northeastern United States. +In 1607 and 1608, Hudson made two attempts on behalf of English merchants to find a rumoured Northeast Passage to Cathay via a route above the Arctic Circle. In 1609, he landed in North America on behalf of the Dutch East India Company and explored the region around the modern New York metropolitan area. Looking for a Northwest Passage to Asia on his ship Halve Maen ("Half Moon"), he sailed up the Hudson River, which was later named after him, and thereby laid the foundation for Dutch colonization of the region. His contributions to the exploration of the New World were significant and lasting. His voyages helped to establish European contact with the native peoples of North America and contributed to the development of trade and commerce. +On his final expedition, while still searching for the Northwest Passage, Hudson became the first European to see Hudson Strait and the immense Hudson Bay. In 1611, after wintering on the shore of James Bay, Hudson wanted to press on to the west, but most of his crew mutinied. The mutineers cast Hudson, his son, and six others adrift; what then happened to the Hudsons and their companions is unknown. +Thought: Henry Hudson discovered the Hudson River in 1609. He was born around 1565. Action: -[{"name": "Search", "arguments": {"topic": "Henry Hudson birth year"}}] -Observation: Henry Hudson was born in 1565. -Thought: To find out how many years ago he was born, I subtract his birth year from the current year (2025). -Action: -[{"name": "Calc", "arguments": {"expr": "2025 - 1565"}}] -Observation: The result of the calculation is 460. -Thought: Henry Hudson was born 460 years ago.Action: -[{"name": "Finish", "arguments": {"topic": "460 years ago"}}] +[{"name": "Calc", "arguments": {"expr": "2025 - 1565"}}] +Observation: 460 +Henry Hudson was born around 1565, so he was approximately 460 years ago.Action: +[{"name": "Finish", "arguments": {"topic": "460"}}] Observation: diff --git a/tests/results/examples/react/demo.0.result b/tests/results/examples/react/demo.0.result index 2623d9115..ce67247d3 100644 --- a/tests/results/examples/react/demo.0.result +++ b/tests/results/examples/react/demo.0.result @@ -1,15 +1,13 @@ -How many years ago was the discoverer of the Hudson River born? Keep in mind we are in 2025. -Thought: I need to find out who discovered the Hudson River and then calculate how many years ago they were born, given that it's currently 2025. +How many years ago was the discoverer of the Hudson River born? Keep in mind we are in 2025. When searching for a birthday for a person, simply ask for the name of that person. +Thought: I need to find out who discovered the Hudson River and then calculate how many years ago their birthdate was. Action: -[{"name": "Search", "arguments": {"topic": "Hudson River discoverer"}}] -Observation: The Hudson River was discovered by Henry Hudson in 1609. -Thought: Henry Hudson was born around 1565 to 1570. I need to find the exact birth year and then calculate how many years ago it was from 2025. +[{"name": "Search", "arguments": {"topic": "Henry Hudson"}}] +Observation: Henry Hudson (c. 1565 – disappeared 23 June 1611) was an English sea explorer and navigator during the early 17th century, best known for his explorations of present-day Canada and parts of the Northeastern United States. +In 1607 and 1608, Hudson made two attempts on behalf of English merchants to find a rumoured Northeast Passage to Cathay via a route above the Arctic Circle. In 1609, he landed in North America on behalf of the Dutch East India Company and explored the region around the modern New York metropolitan area. Looking for a Northwest Passage to Asia on his ship Halve Maen ("Half Moon"), he sailed up the Hudson River, which was later named after him, and thereby laid the foundation for Dutch colonization of the region. His contributions to the exploration of the New World were significant and lasting. His voyages helped to establish European contact with the native peoples of North America and contributed to the development of trade and commerce. +On his final expedition, while still searching for the Northwest Passage, Hudson became the first European to see Hudson Strait and the immense Hudson Bay. In 1611, after wintering on the shore of James Bay, Hudson wanted to press on to the west, but most of his crew mutinied. The mutineers cast Hudson, his son, and six others adrift; what then happened to the Hudsons and their companions is unknown. +Thought: Henry Hudson discovered the Hudson River in 1609. He was born around 1565. Action: -[{"name": "Search", "arguments": {"topic": "Henry Hudson birth year"}}] -Observation: Henry Hudson was born in 1565. -Thought: To find out how many years ago he was born, I subtract his birth year from the current year (2025). -Action: -[{"name": "Calc", "arguments": {"expr": "2025 - 1565"}}] -Observation: The result of the calculation is 460. -Thought: Henry Hudson was born 460 years ago.Action: -[{"name": "Finish", "arguments": {"topic": "460 years ago"}}] +[{"name": "Calc", "arguments": {"expr": "2025 - 1565"}}] +Observation: 460 +Henry Hudson was born around 1565, so he was approximately 460 years ago.Action: +[{"name": "Finish", "arguments": {"topic": "460"}}] diff --git a/tests/test_ast_utils.py b/tests/test_ast_utils.py index 36b6b1796..27c0a0444 100644 --- a/tests/test_ast_utils.py +++ b/tests/test_ast_utils.py @@ -25,11 +25,11 @@ class MapCounter: def __init__(self): self.cpt = 0 - def count(map_self, ast): # pylint: disable=no-self-argument + def count(map_self, ast): # pylint: disable=no-self-argument # type: ignore map_self.cpt += 1 class C(MappedFunctions): - def f_block(_, block): # pylint: disable=no-self-argument + def f_block(_, block): # pylint: disable=no-self-argument # type: ignore return map_self.count(block) _ = map_block_children(C(), ast) diff --git a/tests/test_examples_run.py b/tests/test_examples_run.py index d3e08689a..326d291d5 100644 --- a/tests/test_examples_run.py +++ b/tests/test_examples_run.py @@ -46,6 +46,11 @@ pathlib.Path("pdl-live-react") / "demos" / "error.pdl", pathlib.Path("pdl-live-react") / "demos" / "demo1.pdl", pathlib.Path("pdl-live-react") / "demos" / "demo2.pdl", + pathlib.Path("pdl-live-react") + / "src-tauri" + / "tests" + / "cli" + / "read-stdin.pdl", # For now, skip the granite-io examples pathlib.Path("examples") / "granite-io" / "granite_io_hallucinations.pdl", pathlib.Path("examples") / "granite-io" / "granite_io_openai.pdl",