diff --git a/common/src/buttercup/common/llm.py b/common/src/buttercup/common/llm.py index cfc0fb57..3379e4e1 100644 --- a/common/src/buttercup/common/llm.py +++ b/common/src/buttercup/common/llm.py @@ -86,7 +86,7 @@ def get_langfuse_callbacks() -> list[BaseCallbackHandler]: def create_default_llm(**kwargs: Any) -> BaseChatModel: """Create an LLM object with the default configuration.""" return create_llm( - model_name=ButtercupLLM.OPENAI_GPT_4O.value, + model_name=kwargs.pop("model_name", ButtercupLLM.OPENAI_GPT_4O.value), temperature=kwargs.pop("temperature", 0.1), timeout=420.0, max_retries=3, diff --git a/common/src/buttercup/common/reproduce_multiple.py b/common/src/buttercup/common/reproduce_multiple.py index c8a4832d..4c3db841 100644 --- a/common/src/buttercup/common/reproduce_multiple.py +++ b/common/src/buttercup/common/reproduce_multiple.py @@ -17,8 +17,8 @@ class ReproduceMultiple: with task.get_rw_copy(self.wdir) as local_task: yield (build, local_task.reproduce_pov(harness_name, pov)) - def get_first_crash(self) -> tuple[BuildOutput, ReproduceResult] | None: - for build, result in self.attempt_reproduce(): + def get_first_crash(self, pov: Path, harness_name: str) -> tuple[BuildOutput, ReproduceResult] | None: + for build, result in self.attempt_reproduce(pov, harness_name): if result.command_result.returncode is not None and result.stacktrace() is not None and result.did_crash(): return build, result return None diff --git a/seed-gen/eval/configs/libpng_model_eval_config.json b/seed-gen/eval/configs/libpng_model_eval_config.json new file mode 100644 index 00000000..c75f9f60 --- /dev/null +++ b/seed-gen/eval/configs/libpng_model_eval_config.json @@ -0,0 +1,50 @@ +{ + "configs": [ + { + "name": "gpt-4o", + "llm_kwargs": { + "model_name": "openai-gpt-4o", + "temperature": 0.1, + "timeout": 420.0, + "max_retries": 3 + } + }, + { + "name": "gpt-4o-mini", + "llm_kwargs": { + "model_name": "openai-gpt-4o-mini", + "temperature": 0.1, + "timeout": 420.0, + "max_retries": 3 + } + }, + { + "name": "o3-mini", + "llm_kwargs": { + "model_name": "openai-o3-mini", + "timeout": 420.0, + "max_retries": 3 + } + }, + { + "name": "o1", + "llm_kwargs": { + "model_name": "openai-o1", + "timeout": 420.0, + "max_retries": 3 + } + }, + { + "name": "3.5-sonnet", + "llm_kwargs": { + "model_name": "claude-3.5-sonnet", + "temperature": 0.1, + "timeout": 420.0, + "max_retries": 3 + } + } + ], + "attempts_per_config": 1, + "package_name": "libpng", + "harness_name": "libpng_read_fuzzer" +} diff --git a/seed-gen/eval/vuln_discovery_base.py b/seed-gen/eval/vuln_discovery_base.py new file mode 100644 index 00000000..2f40bc6f --- /dev/null +++ b/seed-gen/eval/vuln_discovery_base.py @@ -0,0 +1,187 @@ +""" +Evaluate a prompt with different configurations for vulnerability discovery + +Example usage: +python seed-gen/eval/vuln_discovery_base.py + --prompt seed-gen/eval-prompt.txt \ + --eval-config seed-gen/eval/configs/libpng_model_eval_config.json \ + --task-dir sample-task-libpng/162fc720-b291-48cc-9554-efd8207cfb16-e408da6a-5c31-4d/ \ + --out-dir model-eval-out +""" + +import argparse +import json +import shutil +import tempfile +from abc import ABC, abstractmethod +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Any + +from langchain_core.runnables import RunnableConfig +from tqdm import tqdm + +from buttercup.common.challenge_task import ChallengeTask, ChallengeTaskError +from buttercup.common.llm import create_llm, get_langfuse_callbacks +from buttercup.common.logger import setup_package_logger +from buttercup.seed_gen.sandbox.sandbox import sandbox_exec_funcs +from buttercup.seed_gen.utils import extract_md + +logger = setup_package_logger(__name__, "DEBUG") + + +@dataclass +class TestConfig: + name: str + llm_kwargs: dict[str, Any] + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> "TestConfig": + return cls(**data) + + +@dataclass +class EvalConfig: + configs: list[TestConfig] + attempts_per_config: int + package_name: str + harness_name: str + + @classmethod + def from_json(cls, json_path: str) -> "EvalConfig": + with open(json_path) as f: + data = json.load(f) + + # Convert the configs list from dicts to TestConfig objects + data["configs"] = [TestConfig.from_dict(c) for c in data["configs"]] + return cls(**data) + + +class VulnDiscoveryEvaluatorBase(ABC): + def __init__(self, task_dir: Path, out_dir: Path, eval_config: EvalConfig): + """Initialize the evaluator. + + Args: + task_dir: Path to the challenge task directory + out_dir: Path to the output directory + eval_config: Evaluation configuration + """ + self.task_dir = task_dir + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + out_dir = out_dir / f"run-{timestamp}" + self.out_dir = out_dir + self.eval_config = EvalConfig.from_json(eval_config) + ## set up out dir + self.out_dir.mkdir(parents=True) + self.metadata = { + "package_name": self.eval_config.package_name, + "harness_name": self.eval_config.harness_name, + "eval_type": "vuln-discovery", + } + shutil.copy(eval_config, self.out_dir / "config.json") + + def test_povs(self, rw_task: ChallengeTask, pov_dir: Path) -> tuple[list[Path], list[Path]]: + valid_povs = [] + invalid_povs = [] + for pov in pov_dir.iterdir(): + try: + pov_output = rw_task.reproduce_pov(self.eval_config.harness_name, pov) + if not pov_output.did_crash(): + valid_povs.append(pov) + else: + invalid_povs.append(pov) + except ChallengeTaskError as exc: + logger.error(f"Error reproducing PoV {pov}: {exc}") + return valid_povs, invalid_povs + + def evaluate_pov_generation(self) -> dict[str, Any]: + """ + Evaluate test configurations for vulnerability discovery. + """ + + # Create challenge task for verification + chall_task = ChallengeTask(read_only_task_dir=self.task_dir) + with chall_task.get_rw_copy(work_dir=None) as rw_task: + for config in tqdm(self.eval_config.configs, desc="Evaluating configs"): + config_dir = self.out_dir / config.name + config_dir.mkdir() + for i in tqdm( + range(self.eval_config.attempts_per_config), desc=f"Evaluating {config.name}" + ): + pov_dir = config_dir / f"attempt_{i}" + pov_dir.mkdir() + with tempfile.TemporaryDirectory() as workdir_str: + workdir = Path(workdir_str) + try: + # Generate PoV using the prompt + pov_funcs, trace_id = self.generate_pov_funcs(config) + trace_id_file = pov_dir / "langfuse_trace_id" + trace_id_file.write_text(trace_id) + sandbox_exec_funcs(pov_funcs, workdir) + valid_povs, invalid_povs = self.test_povs(rw_task, workdir) + valid_pov_count = len(valid_povs) + total_pov_count = valid_pov_count + len(invalid_povs) + logger.info( + f"Attempt {i}: generated {valid_pov_count} valid PoVs of {total_pov_count} total" # noqa: E501 + ) + valid_dir = pov_dir / "valid" + invalid_dir = pov_dir / "invalid" + valid_dir.mkdir() + invalid_dir.mkdir() + for pov in valid_povs: + shutil.copy(pov, valid_dir / pov.name) + for pov in invalid_povs: + shutil.copy(pov, invalid_dir / pov.name) + except Exception as e: + logger.error(f"Error during PoV generation: {str(e)}") + + @abstractmethod + def generate_pov_funcs(self, config: TestConfig) -> tuple[str, str]: + """Generate a string with PoV functions from a config""" + pass + + +class VulnDiscoveryEvaluator(VulnDiscoveryEvaluatorBase): + def __init__(self, task_dir: Path, out_dir: Path, eval_config: EvalConfig, prompt: Path): + """Initialize the evaluator. + + Args: + task_dir: Path to the challenge task directory + out_dir: Path to the output directory + eval_config: Evaluation configuration + prompt: Path to the prompt file + """ + super().__init__(task_dir, out_dir, eval_config) + self.prompt = prompt.read_text() + + def generate_pov_funcs(self, config: TestConfig) -> tuple[str, str]: + llm_callbacks = get_langfuse_callbacks() + llm = create_llm(**config.llm_kwargs, callbacks=llm_callbacks) + chain = llm | extract_md + chain_config = chain.with_config(RunnableConfig(metadata=self.metadata)) + res = chain_config.invoke(self.prompt) + trace_id = llm_callbacks[0].trace.trace_id + return res, trace_id + + +def main(): + """Main function to run the evaluation.""" + parser = argparse.ArgumentParser(description="Eval vuln discovery effectiveness") + parser.add_argument("--prompt", required=True, help="Path to prompt file", type=Path) + parser.add_argument( + "--eval-config", required=True, help="Path to evaluation json config", type=Path + ) + parser.add_argument( + "--task-dir", required=True, help="Path to (built) challenge task directory", type=Path + ) + parser.add_argument("--out-dir", required=True, help="Eval output directory", type=Path) + args = parser.parse_args() + args.out_dir.mkdir(parents=True, exist_ok=True) + + evaluator = VulnDiscoveryEvaluator(args.task_dir, args.out_dir, args.eval_config, args.prompt) + evaluator.evaluate_pov_generation() + + +if __name__ == "__main__": + main() diff --git a/seed-gen/eval/vuln_discovery_current.py b/seed-gen/eval/vuln_discovery_current.py new file mode 100644 index 00000000..5cbd87e2 --- /dev/null +++ b/seed-gen/eval/vuln_discovery_current.py @@ -0,0 +1,52 @@ +""" +Evaluate vulnerability discovery with different configurations +""" + +import argparse +from pathlib import Path + +from vuln_discovery_base import TestConfig, VulnDiscoveryEvaluatorBase + +from buttercup.common.llm import create_llm, get_langfuse_callbacks +from buttercup.seed_gen.mock_context.mock import get_diff, get_harness +from buttercup.seed_gen.tasks import VULN_DISCOVERY_MAX_POV_COUNT +from buttercup.seed_gen.vuln_discovery import VulnDiscovery + + +class VulnDiscoveryEvaluator(VulnDiscoveryEvaluatorBase): + def generate_pov_funcs(self, config: TestConfig) -> tuple[str, str]: + """Generate PoV functions""" + llm_callbacks = get_langfuse_callbacks() + llm = create_llm(**config.llm_kwargs, callbacks=llm_callbacks) + vuln_discovery = VulnDiscovery(llm) + + harness = get_harness(self.eval_config.package_name) + diff = get_diff(self.eval_config.package_name) + analysis = vuln_discovery.analyze_diff(diff, harness) + + pov_funcs = vuln_discovery.write_pov_funcs( + analysis=analysis, harness=harness, diff=diff, max_povs=VULN_DISCOVERY_MAX_POV_COUNT + ) + trace_id = llm_callbacks[0].trace.trace_id + return pov_funcs, trace_id + + +def main(): + """Main function to run the evaluation.""" + parser = argparse.ArgumentParser(description="Eval multi-step vuln discovery effectiveness") + parser.add_argument( + "--eval-config", required=True, help="Path to evaluation json config", type=Path + ) + parser.add_argument( + "--task-dir", required=True, help="Path to (built) challenge task directory", type=Path + ) + parser.add_argument("--out-dir", required=True, help="Eval output directory", type=Path) + args = parser.parse_args() + args.out_dir.mkdir(parents=True, exist_ok=True) + + evaluator = VulnDiscoveryEvaluator(args.task_dir, args.out_dir, args.eval_config) + evaluator.evaluate_pov_generation() + + +if __name__ == "__main__": + main() diff --git a/seed-gen/pyproject.toml b/seed-gen/pyproject.toml index a350cf9c..bac34155 100644 --- a/seed-gen/pyproject.toml +++ b/seed-gen/pyproject.toml @@ -24,19 +24,12 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project.optional-dependencies] -doc = [] -test = ["pytest", "pytest-cov", "pretend", "coverage[toml]"] -lint = [ - # NOTE: ruff is under active development, so we pin conservatively here - # and let Dependabot periodically perform this update. +dev = [ + "mypy ~= 1.15.0", + "pytest ~= 8.3.4", "ruff ~= 0.6.2", - "mypy >= 1.0", - "types-html5lib", - "types-requests", - "types-toml", - "interrogate", + "tqdm ~= 4.67.1", ] -dev = ["seed-gen[doc,test,lint]", "twine", "build"] [project.scripts] "seed-gen" = "buttercup.seed_gen._cli:main" @@ -66,7 +59,7 @@ warn_unused_ignores = true [tool.ruff] line-length = 100 -include = ["src/**/*.py", "test/**/*.py"] +include = ["src/**/*.py", "test/**/*.py", "eval/**/*.py"] [tool.ruff.lint] select = ["E", "F", "I", "W", "UP"] diff --git a/seed-gen/src/buttercup/seed_gen/prompts.py b/seed-gen/src/buttercup/seed_gen/prompts.py index 667c7d67..547e9f74 100644 --- a/seed-gen/src/buttercup/seed_gen/prompts.py +++ b/seed-gen/src/buttercup/seed_gen/prompts.py @@ -51,32 +51,24 @@ The python functions are: """ DIFF_ANALYSIS_SYSTEM_PROMPT = """ -You are a security engineer. Your job is to analyze whether a diff introduces a vulnerability in a project. +You are a security engineer. Your job is to analyze the vulnerability introduced by a commit diff in a project. """ DIFF_ANALYSIS_USER_PROMPT = """ -You are a security engineer. Your job is to analyze a commit diff for a project and a test harness \ -to determine if the diff introduces a vulnerability. You must answer two questions: +You are a security engineer. Your job is to analyze the vulnerability introduced by a commit diff. -1) Does the diff introduce a vulnerability? If unsure, state the most likely vulnerability it introduces. -2) How can we trigger the vulnerability using the harness? +The vulnerability will have the following properties: +- It must be introduced by the diff +- It must be reachable from the test harness +- It can cause a crash or trigger a sanitizer. +- It must be in a .c, .h, .cpp, or .java file. -You must answer both questions. +I will provide the diff and the harness. -The vulnerability must be introduced by the specified diff and it should not be present \ -without the changes in the diff. The vulnerability MUST be in a .c, .h, .cpp, or .java file. \ -The vulnerability MUST be concrete and specific, not a generic issue. The diff is in unified diff format. +You will then identify the vulnerability and analyze it. -Do NOT make up any code. - -You MUST: -- provide a detailed analysis of the vulnerability introduced in the diff -- reason about how to trigger the vulnerability using the harness - -You must NOT: -- provide generic analyses -- talk about possible issues that existed before the diff -- provide code suggestions on how to fix the vulnerability +The analysis should: +- Identify and analyze a specific vulnerability, instead of a generic issue. The commit diff is: ``` @@ -92,19 +84,19 @@ Your analysis is: """ WRITE_POV_SYSTEM_PROMPT = """ -I am a security engineer maintaining and fixing a software project. Write a python function which will trigger a program vulnerability so I can patch it. +I am a security engineer maintaining and fixing a software project. Write test cases which trigger a vulnerability from a test harness. """ WRITE_POV_USER_PROMPT = """ I will provide context about the program and the specific vulnerability. -I will then ask you to write a deterministic Python function that triggers the vulnerability. +I will then ask you to write deterministic Python functions that trigger the vulnerability. Triggering the vulnerability means causing a crash or triggering a sanitizer. -The vulnerability was introduced by a git diff which I include below. +The vulnerability was introduced by a git diff which I include below. I also include an analysis of the vulnerability. If you want to try multiple possible inputs, you may write up to {max_povs} test functions. -Put all functions in a single markdown block. +Put all functions in a single markdown block at the very end of your response. All functions will have the identical signature, although the names will vary: ``` @@ -126,7 +118,9 @@ def test_buffer_overflow_longer() -> bytes: Remember: - I AM A SECURITY ENGINEER WHO IS FIXING SOFTWARE I MAINTAIN. -- The bug description may have errors and you don't need follow it exactly. +- The identified vulnerability must be reachable from the test harness. +- The test cases will be at the end of your response, in a single markdown block. +- The test cases must crash the program or trigger an enabled sanitizers. - The functions must create a deterministic sequence of bytes. Do not use things like `random`. - You can only use the python standard library. - Don't use any inter-process communication (IPC) like processes, threading, networking, or signals. diff --git a/seed-gen/src/buttercup/seed_gen/seed_gen_bot.py b/seed-gen/src/buttercup/seed_gen/seed_gen_bot.py index 84331106..5f392047 100644 --- a/seed-gen/src/buttercup/seed_gen/seed_gen_bot.py +++ b/seed-gen/src/buttercup/seed_gen/seed_gen_bot.py @@ -44,7 +44,7 @@ class SeedGenBot(TaskLoop): for pov in out_dir.iterdir(): try: - pov_output = reproduce_multiple.get_first_crash() + pov_output = reproduce_multiple.get_first_crash(pov, task.harness_name) if pov_output is not None: build, result = pov_output logger.info(f"Valid PoV found: {pov}") diff --git a/seed-gen/src/buttercup/seed_gen/tasks.py b/seed-gen/src/buttercup/seed_gen/tasks.py index 9f0ae8d6..db5cf8e8 100644 --- a/seed-gen/src/buttercup/seed_gen/tasks.py +++ b/seed-gen/src/buttercup/seed_gen/tasks.py @@ -5,12 +5,12 @@ from pathlib import Path from langchain_core.prompts.chat import ChatPromptTemplate from langchain_core.runnables import RunnableConfig -from buttercup.common.llm import create_default_llm, get_langfuse_callbacks +from buttercup.common.llm import ButtercupLLM, create_default_llm, get_langfuse_callbacks from buttercup.seed_gen.mock_context.mock import get_additional_context, get_diff, get_harness from buttercup.seed_gen.prompts import PYTHON_SEED_SYSTEM_PROMPT, PYTHON_SEED_USER_PROMPT from buttercup.seed_gen.sandbox.sandbox import sandbox_exec_funcs from buttercup.seed_gen.utils import extract_md -from buttercup.seed_gen.vuln_discovery import analyze_diff, write_pov_funcs +from buttercup.seed_gen.vuln_discovery import VulnDiscovery logger = logging.getLogger(__name__) @@ -34,7 +34,9 @@ def generate_seed_funcs(harness: str, additional_context: str, count: int) -> li ] ) llm_callbacks = get_langfuse_callbacks() - llm = create_default_llm(callbacks=llm_callbacks) + llm = create_default_llm( + model_name=ButtercupLLM.CLAUDE_3_5_SONNET.value, callbacks=llm_callbacks + ) chain = prompt | llm | extract_md chain_config = chain.with_config(RunnableConfig(tags=["generate_seed_funcs"])) funcs = chain_config.invoke( @@ -70,14 +72,19 @@ def do_seed_explore() -> None: def do_vuln_discovery(challenge: str, output_dir: Path) -> None: """Do vuln-discovery task""" logger.info("Doing vuln-discovery for challenge %s", challenge) + llm_callbacks = get_langfuse_callbacks() + llm = create_default_llm( + model_name=ButtercupLLM.CLAUDE_3_5_SONNET.value, callbacks=llm_callbacks + ) + vuln_discovery = VulnDiscovery(llm) max_povs = VULN_DISCOVERY_MAX_POV_COUNT harness = get_harness(challenge) diff = get_diff(challenge) try: logger.info("Analyzing the diff in challenge %s", challenge) - analysis = analyze_diff(diff, harness) + analysis = vuln_discovery.analyze_diff(diff, harness) logger.info("Making PoVs for the challenge %s", challenge) - pov_funcs = write_pov_funcs(analysis, harness, diff, max_povs) + pov_funcs = vuln_discovery.write_pov_funcs(analysis, harness, diff, max_povs) sandbox_exec_funcs(pov_funcs, output_dir) except Exception as err: logger.error("Failed vuln-discovery for challenge %s: %s", challenge, str(err)) diff --git a/seed-gen/src/buttercup/seed_gen/utils.py b/seed-gen/src/buttercup/seed_gen/utils.py index 83937992..d104f5e9 100644 --- a/seed-gen/src/buttercup/seed_gen/utils.py +++ b/seed-gen/src/buttercup/seed_gen/utils.py @@ -20,7 +20,7 @@ def resolve_module_subpath(subpath: str) -> Path: def extract_md(msg: AIMessage) -> str: - """Extract the markdown from the AI message.""" + """Extract last markdown block from the AI message.""" if not isinstance(msg, AIMessage): raise OutputParserException( "extract_md: did not receive an AIMessage. Received: %s", type(msg) @@ -32,7 +32,12 @@ def extract_md(msg: AIMessage) -> str: "extract_md: content is not a string. Content is %s", type(content) ) - match = re.search(r"```([A-Za-z]*)\n(.*?)```", content, re.DOTALL) + # get last markdown block + find_iter = re.finditer(r"```([A-Za-z]*)\n(.*?)```", content, re.DOTALL) + match = None + for m in find_iter: + match = m + if match is not None: content = match.group(2) else: diff --git a/seed-gen/src/buttercup/seed_gen/vuln_discovery.py b/seed-gen/src/buttercup/seed_gen/vuln_discovery.py index 1ac37a13..bfb31203 100644 --- a/seed-gen/src/buttercup/seed_gen/vuln_discovery.py +++ b/seed-gen/src/buttercup/seed_gen/vuln_discovery.py @@ -1,10 +1,10 @@ import logging +from langchain_core.language_models import BaseChatModel from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts.chat import ChatPromptTemplate from langchain_core.runnables import RunnableConfig -from buttercup.common.llm import create_default_llm, get_langfuse_callbacks from buttercup.seed_gen.prompts import ( DIFF_ANALYSIS_SYSTEM_PROMPT, DIFF_ANALYSIS_USER_PROMPT, @@ -16,49 +16,48 @@ from buttercup.seed_gen.utils import extract_md logger = logging.getLogger(__name__) -def analyze_diff(diff: str, harness: str) -> str: - """ - Analyze a diff for a project and a test harness to determine if the diff introduces a vuln. - """ - llm_callbacks = get_langfuse_callbacks() - llm = create_default_llm(callbacks=llm_callbacks) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", DIFF_ANALYSIS_SYSTEM_PROMPT), - ("human", DIFF_ANALYSIS_USER_PROMPT), - ] - ) - chain = prompt | llm | StrOutputParser() - chain_config = chain.with_config(RunnableConfig(tags=["analyze_diff"])) - analysis = chain_config.invoke( - { - "diff": diff, - "harness": harness, - } - ) - return analysis +class VulnDiscovery: + def __init__(self, llm: BaseChatModel): + self.llm = llm + def analyze_diff(self, diff: str, harness: str) -> str: + """ + Analyze a diff for a project and a test harness to determine if the diff introduces a vuln. + """ + prompt = ChatPromptTemplate.from_messages( + [ + ("system", DIFF_ANALYSIS_SYSTEM_PROMPT), + ("human", DIFF_ANALYSIS_USER_PROMPT), + ] + ) + chain = prompt | self.llm | StrOutputParser() + chain_config = chain.with_config(RunnableConfig(tags=["analyze_diff"])) + analysis = chain_config.invoke( + { + "diff": diff, + "harness": harness, + } + ) + return analysis -def write_pov_funcs(analysis: str, harness: str, diff: str, max_povs: int) -> str: - """ - Write PoVs for a vulnerability. - """ - llm_callbacks = get_langfuse_callbacks() - llm = create_default_llm(callbacks=llm_callbacks) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", WRITE_POV_SYSTEM_PROMPT), - ("human", WRITE_POV_USER_PROMPT), - ] - ) - chain = prompt | llm | extract_md - chain_config = chain.with_config(RunnableConfig(tags=["write_pov_funcs"])) - pov_funcs = chain_config.invoke( - { - "analysis": analysis, - "harness": harness, - "diff": diff, - "max_povs": max_povs, - } - ) - return pov_funcs + def write_pov_funcs(self, analysis: str, harness: str, diff: str, max_povs: int) -> str: + """ + Write PoVs for a vulnerability. + """ + prompt = ChatPromptTemplate.from_messages( + [ + ("system", WRITE_POV_SYSTEM_PROMPT), + ("human", WRITE_POV_USER_PROMPT), + ] + ) + chain = prompt | self.llm | extract_md + chain_config = chain.with_config(RunnableConfig(tags=["write_pov_funcs"])) + pov_funcs = chain_config.invoke( + { + "analysis": analysis, + "harness": harness, + "diff": diff, + "max_povs": max_povs, + } + ) + return pov_funcs diff --git a/seed-gen/test/test_utils.py b/seed-gen/test/test_utils.py new file mode 100644 index 00000000..af4df656 --- /dev/null +++ b/seed-gen/test/test_utils.py @@ -0,0 +1,30 @@ +from langchain_core.messages import AIMessage + +from buttercup.seed_gen.utils import extract_md + + +def test_extract_md_no_markdown(): + message = AIMessage(content="This is a message with no markdown blocks") + result = extract_md(message) + assert result == "This is a message with no markdown blocks" + + +def test_extract_md_single_block(): + message = AIMessage( + content="Some text before\n```python\nprint('hello')\nprint('hello')\n```\nText after" + ) + result = extract_md(message) + assert result == "print('hello')\nprint('hello')\n" + + +def test_extract_md_multiple_blocks(): + message = AIMessage( + content=( + "First block:\n" + "```python\nprint('first')\n```\n" + "Middle text\n" + "```python\nprint('second')\n```" + ) + ) + result = extract_md(message) + assert result == "print('second')\n" diff --git a/seed-gen/uv.lock b/seed-gen/uv.lock index 1970899a..7add67c7 100644 --- a/seed-gen/uv.lock +++ b/seed-gen/uv.lock @@ -100,20 +100,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] -[[package]] -name = "build" -version = "1.2.2.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, - { name = "packaging" }, - { name = "pyproject-hooks" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950 }, -] - [[package]] name = "cachetools" version = "4.2.4" @@ -191,18 +177,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, -] - [[package]] name = "clusterfuzz" version = "2.6.0" @@ -280,54 +254,6 @@ dev = [ { name = "ruff", specifier = ">=0.9.2" }, ] -[[package]] -name = "coverage" -version = "7.6.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/d6/2b53ab3ee99f2262e6f0b8369a43f6d66658eab45510331c0b3d5c8c4272/coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", size = 805941 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/7f/4af2ed1d06ce6bee7eafc03b2ef748b14132b0bdae04388e451e4b2c529b/coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", size = 208645 }, - { url = "https://files.pythonhosted.org/packages/dc/60/d19df912989117caa95123524d26fc973f56dc14aecdec5ccd7d0084e131/coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", size = 208898 }, - { url = "https://files.pythonhosted.org/packages/bd/10/fecabcf438ba676f706bf90186ccf6ff9f6158cc494286965c76e58742fa/coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", size = 242987 }, - { url = "https://files.pythonhosted.org/packages/4c/53/4e208440389e8ea936f5f2b0762dcd4cb03281a7722def8e2bf9dc9c3d68/coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", size = 239881 }, - { url = "https://files.pythonhosted.org/packages/c4/47/2ba744af8d2f0caa1f17e7746147e34dfc5f811fb65fc153153722d58835/coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", size = 242142 }, - { url = "https://files.pythonhosted.org/packages/e9/90/df726af8ee74d92ee7e3bf113bf101ea4315d71508952bd21abc3fae471e/coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", size = 241437 }, - { url = "https://files.pythonhosted.org/packages/f6/af/995263fd04ae5f9cf12521150295bf03b6ba940d0aea97953bb4a6db3e2b/coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", size = 239724 }, - { url = "https://files.pythonhosted.org/packages/1c/8e/5bb04f0318805e190984c6ce106b4c3968a9562a400180e549855d8211bd/coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", size = 241329 }, - { url = "https://files.pythonhosted.org/packages/9e/9d/fa04d9e6c3f6459f4e0b231925277cfc33d72dfab7fa19c312c03e59da99/coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", size = 211289 }, - { url = "https://files.pythonhosted.org/packages/53/40/53c7ffe3c0c3fff4d708bc99e65f3d78c129110d6629736faf2dbd60ad57/coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", size = 212079 }, - { url = "https://files.pythonhosted.org/packages/fb/b2/f655700e1024dec98b10ebaafd0cedbc25e40e4abe62a3c8e2ceef4f8f0a/coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", size = 200552 }, -] - -[[package]] -name = "cryptography" -version = "44.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/67/545c79fe50f7af51dbad56d16b23fe33f63ee6a5d956b3cb68ea110cbe64/cryptography-44.0.1.tar.gz", hash = "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14", size = 710819 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/b9/4d1fa8d73ae6ec350012f89c3abfbff19fc95fe5420cf972e12a8d182986/cryptography-44.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f", size = 3943865 }, - { url = "https://files.pythonhosted.org/packages/6e/57/371a9f3f3a4500807b5fcd29fec77f418ba27ffc629d88597d0d1049696e/cryptography-44.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2", size = 4162562 }, - { url = "https://files.pythonhosted.org/packages/c5/1d/5b77815e7d9cf1e3166988647f336f87d5634a5ccecec2ffbe08ef8dd481/cryptography-44.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911", size = 3951923 }, - { url = "https://files.pythonhosted.org/packages/28/01/604508cd34a4024467cd4105887cf27da128cba3edd435b54e2395064bfb/cryptography-44.0.1-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69", size = 3685194 }, - { url = "https://files.pythonhosted.org/packages/c6/3d/d3c55d4f1d24580a236a6753902ef6d8aafd04da942a1ee9efb9dc8fd0cb/cryptography-44.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026", size = 4187790 }, - { url = "https://files.pythonhosted.org/packages/ea/a6/44d63950c8588bfa8594fd234d3d46e93c3841b8e84a066649c566afb972/cryptography-44.0.1-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd", size = 3951343 }, - { url = "https://files.pythonhosted.org/packages/c1/17/f5282661b57301204cbf188254c1a0267dbd8b18f76337f0a7ce1038888c/cryptography-44.0.1-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0", size = 4187127 }, - { url = "https://files.pythonhosted.org/packages/f3/68/abbae29ed4f9d96596687f3ceea8e233f65c9645fbbec68adb7c756bb85a/cryptography-44.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf", size = 4070666 }, - { url = "https://files.pythonhosted.org/packages/0f/10/cf91691064a9e0a88ae27e31779200b1505d3aee877dbe1e4e0d73b4f155/cryptography-44.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864", size = 4288811 }, - { url = "https://files.pythonhosted.org/packages/ba/9f/1775600eb69e72d8f9931a104120f2667107a0ee478f6ad4fe4001559345/cryptography-44.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862", size = 3943269 }, - { url = "https://files.pythonhosted.org/packages/25/ba/e00d5ad6b58183829615be7f11f55a7b6baa5a06910faabdc9961527ba44/cryptography-44.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3", size = 4166461 }, - { url = "https://files.pythonhosted.org/packages/b3/45/690a02c748d719a95ab08b6e4decb9d81e0ec1bac510358f61624c86e8a3/cryptography-44.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7", size = 3950314 }, - { url = "https://files.pythonhosted.org/packages/e6/50/bf8d090911347f9b75adc20f6f6569ed6ca9b9bff552e6e390f53c2a1233/cryptography-44.0.1-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a", size = 3686675 }, - { url = "https://files.pythonhosted.org/packages/e1/e7/cfb18011821cc5f9b21efb3f94f3241e3a658d267a3bf3a0f45543858ed8/cryptography-44.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c", size = 4190429 }, - { url = "https://files.pythonhosted.org/packages/07/ef/77c74d94a8bfc1a8a47b3cafe54af3db537f081742ee7a8a9bd982b62774/cryptography-44.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62", size = 3950039 }, - { url = "https://files.pythonhosted.org/packages/6d/b9/8be0ff57c4592382b77406269b1e15650c9f1a167f9e34941b8515b97159/cryptography-44.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41", size = 4189713 }, - { url = "https://files.pythonhosted.org/packages/78/e1/4b6ac5f4100545513b0847a4d276fe3c7ce0eacfa73e3b5ebd31776816ee/cryptography-44.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b", size = 4071193 }, - { url = "https://files.pythonhosted.org/packages/3d/cb/afff48ceaed15531eab70445abe500f07f8f96af2bb35d98af6bfa89ebd4/cryptography-44.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7", size = 4289566 }, -] - [[package]] name = "distro" version = "1.9.0" @@ -346,15 +272,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, ] -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, -] - [[package]] name = "frozenlist" version = "1.5.0" @@ -798,18 +715,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] -[[package]] -name = "id" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611 }, -] - [[package]] name = "idna" version = "3.10" @@ -837,64 +742,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] -[[package]] -name = "interrogate" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "click" }, - { name = "colorama" }, - { name = "py" }, - { name = "tabulate" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/22/74f7fcc96280eea46cf2bcbfa1354ac31de0e60a4be6f7966f12cef20893/interrogate-1.7.0.tar.gz", hash = "sha256:a320d6ec644dfd887cc58247a345054fc4d9f981100c45184470068f4b3719b0", size = 159636 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl", hash = "sha256:b13ff4dd8403369670e2efe684066de9fcb868ad9d7f2b4095d8112142dc9d12", size = 46982 }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777 }, -] - -[[package]] -name = "jaraco-context" -version = "6.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825 }, -] - -[[package]] -name = "jaraco-functools" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/23/9894b3df5d0a6eb44611c36aec777823fc2e07740dabbd0b810e19594013/jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", size = 19159 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/4f/24b319316142c44283d7540e76c7b5a6dbd5db623abd86bb7b3491c21018/jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", size = 10187 }, -] - -[[package]] -name = "jeepney" -version = "0.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/f4/154cf374c2daf2020e05c3c6a03c91348d59b23c5366e968feb198306fdf/jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", size = 106005 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/72/2a1e2290f1ab1e06f71f3d0f1646c9e4634e70e1d37491535e19266e8dc9/jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", size = 48435 }, -] - [[package]] name = "jiter" version = "0.8.2" @@ -936,23 +783,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, ] -[[package]] -name = "keyring" -version = "25.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085 }, -] - [[package]] name = "langchain" version = "0.3.19" @@ -1054,36 +884,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/e4/5380e8229c442e406404977d2ec71a9db6a3e6a89fce7791c6ad7cd2bdbe/langsmith-0.3.8-py3-none-any.whl", hash = "sha256:fbb9dd97b0f090219447fca9362698d07abaeda1da85aa7cc6ec6517b36581b1", size = 332800 }, ] -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - -[[package]] -name = "more-itertools" -version = "10.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/3b/7fa1fe835e2e93fd6d7b52b2f95ae810cf5ba133e1845f726f5a992d62c2/more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b", size = 125009 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/62/0fe302c6d1be1c777cab0616e6302478251dfbf9055ad426f5d0def75c89/more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89", size = 63038 }, -] - [[package]] name = "mozfile" version = "3.0.0" @@ -1173,28 +973,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, ] -[[package]] -name = "nh3" -version = "0.2.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/f2/eb781d94c7855e9129cbbdd3ab09a470441e4176a82a396ae1df270a7333/nh3-0.2.20.tar.gz", hash = "sha256:9705c42d7ff88a0bea546c82d7fe5e59135e3d3f057e485394f491248a1f8ed5", size = 17489 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/08/5e3b61eed1bc0efeb330ddc5cf5194f28a0b7be7943aa20bd44cfe14650b/nh3-0.2.20-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:76e2f603b30c02ff6456b233a83fc377dedab6a50947b04e960a6b905637b776", size = 1202141 }, - { url = "https://files.pythonhosted.org/packages/29/d2/3377f8006c71e95e007b07b5bfcac22c9de4744ca3efb23b396d3deb9581/nh3-0.2.20-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:181063c581defe683bd4bb78188ac9936d208aebbc74c7f7c16b6a32ae2ebb38", size = 760699 }, - { url = "https://files.pythonhosted.org/packages/37/d7/7077f925d7d680d53dcb6e18a4af13d1a7da59761c06c193bfa249a7470a/nh3-0.2.20-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:231addb7643c952cd6d71f1c8702d703f8fe34afcb20becb3efb319a501a12d7", size = 747353 }, - { url = "https://files.pythonhosted.org/packages/cb/59/6b2f32af477aae81f1454a7f6ef490ebc3c22dd9e1370e73fcfe243dc07a/nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1b9a8340a0aab991c68a5ca938d35ef4a8a3f4bf1b455da8855a40bee1fa0ace", size = 854125 }, - { url = "https://files.pythonhosted.org/packages/5b/f2/c3d2f7b801477b8b387b51fbefd16dc7ade888aeac547f18ba0558fd6f48/nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10317cd96fe4bbd4eb6b95f3920b71c902157ad44fed103fdcde43e3b8ee8be6", size = 817453 }, - { url = "https://files.pythonhosted.org/packages/42/4d/f7e3a35506a0eba6eedafc21ad52773985511eb838812e9f96354831ad3c/nh3-0.2.20-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8698db4c04b140800d1a1cd3067fda399e36e1e2b8fc1fe04292a907350a3e9b", size = 891694 }, - { url = "https://files.pythonhosted.org/packages/e6/0e/c499453c296fb40366e3069cd68fde77a10f0a30a17b9d3b491eb3ebc5bf/nh3-0.2.20-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eb04b9c3deb13c3a375ea39fd4a3c00d1f92e8fb2349f25f1e3e4506751774b", size = 744388 }, - { url = "https://files.pythonhosted.org/packages/18/67/c3de8022ba2719bdbbdd3704d1e32dbc7d3f8ac8646247711645fc90d051/nh3-0.2.20-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92f3f1c4f47a2c6f3ca7317b1d5ced05bd29556a75d3a4e2715652ae9d15c05d", size = 764831 }, - { url = "https://files.pythonhosted.org/packages/f0/14/a4ea40e2439717d11c3104fc2dc0ac412301b7aeb81d6a3d0e6505c77e7d/nh3-0.2.20-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddefa9fd6794a87e37d05827d299d4b53a3ec6f23258101907b96029bfef138a", size = 923334 }, - { url = "https://files.pythonhosted.org/packages/ed/ae/e8ee8afaf67903dd304f390056d1ea620327524e2ad66127a331b14d5d98/nh3-0.2.20-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ce3731c8f217685d33d9268362e5b4f770914e922bba94d368ab244a59a6c397", size = 994873 }, - { url = "https://files.pythonhosted.org/packages/20/b5/02122cfe3b36cf0ba0fcd73a04fd462e1f7a9d91b456f6e0b70e46df21c7/nh3-0.2.20-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:09f037c02fc2c43b211ff1523de32801dcfb0918648d8e651c36ef890f1731ec", size = 915707 }, - { url = "https://files.pythonhosted.org/packages/47/d3/5df43cc3570cdc9eb1dc79a39191f89fedf8bcefd8d30a161ff1dffb146c/nh3-0.2.20-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:813f1c8012dd64c990514b795508abb90789334f76a561fa0fd4ca32d2275330", size = 908539 }, - { url = "https://files.pythonhosted.org/packages/4f/fd/aa000f6c76a832c488eac26f20d2e8a221ba2b965efce692f14ebc4290bf/nh3-0.2.20-cp38-abi3-win32.whl", hash = "sha256:47b2946c0e13057855209daeffb45dc910bd0c55daf10190bb0b4b60e2999784", size = 540439 }, - { url = "https://files.pythonhosted.org/packages/19/31/d65594efd3b42b1de2335d576eb77525691fc320dbf8617948ee05c008e5/nh3-0.2.20-cp38-abi3-win_amd64.whl", hash = "sha256:da87573f03084edae8eb87cfe811ec338606288f81d333c07d2a9a0b9b976c0b", size = 541249 }, -] - [[package]] name = "numpy" version = "2.2.3" @@ -1296,15 +1074,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] -[[package]] -name = "pretend" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/7c86fd40c9e83deb10891a60d2dcb1af0b3b38064d72ebdb12486acc824f/pretend-1.0.9.tar.gz", hash = "sha256:c90eb810cde8ebb06dafcb8796f9a95228ce796531bc806e794c2f4649aa1b10", size = 4848 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/1f/3d4f0579913edd3ad5b23ad52fcc42531cb736ad52af2ba6c057da8785b6/pretend-1.0.9-py2.py3-none-any.whl", hash = "sha256:e389b12b7073604be67845dbe32bf8297360ad9a609b24846fe15d86e0b7dc01", size = 3848 }, -] - [[package]] name = "propcache" version = "0.2.1" @@ -1366,15 +1135,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, ] -[[package]] -name = "py" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", size = 207796 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", size = 98708 }, -] - [[package]] name = "pyasn1" version = "0.6.1" @@ -1457,15 +1217,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, ] -[[package]] -name = "pygments" -version = "2.19.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, -] - [[package]] name = "pymemcache" version = "4.0.0" @@ -1504,15 +1255,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, ] -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 }, -] - [[package]] name = "pytest" version = "8.3.4" @@ -1528,19 +1270,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, ] -[[package]] -name = "pytest-cov" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, -] - [[package]] name = "python-dotenv" version = "1.0.1" @@ -1559,15 +1288,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, ] -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 }, -] - [[package]] name = "pyyaml" version = "6.0.2" @@ -1585,20 +1305,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, ] -[[package]] -name = "readme-renderer" -version = "44.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "nh3" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310 }, -] - [[package]] name = "redis" version = "5.2.1" @@ -1671,28 +1377,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, ] -[[package]] -name = "rfc3986" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326 }, -] - -[[package]] -name = "rich" -version = "13.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, -] - [[package]] name = "rsa" version = "4.9" @@ -1730,19 +1414,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/14/fd026bc74ded05e2351681545a5f626e78ef831f8edce064d61acd2e6ec7/ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93", size = 8679879 }, ] -[[package]] -name = "secretstorage" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221 }, -] - [[package]] name = "seed-gen" version = "0.1.0" @@ -1755,51 +1426,20 @@ dependencies = [ [package.optional-dependencies] dev = [ - { name = "build" }, - { name = "coverage" }, - { name = "interrogate" }, { name = "mypy" }, - { name = "pretend" }, { name = "pytest" }, - { name = "pytest-cov" }, { name = "ruff" }, - { name = "twine" }, - { name = "types-html5lib" }, - { name = "types-requests" }, - { name = "types-toml" }, -] -lint = [ - { name = "interrogate" }, - { name = "mypy" }, - { name = "ruff" }, - { name = "types-html5lib" }, - { name = "types-requests" }, - { name = "types-toml" }, -] -test = [ - { name = "coverage" }, - { name = "pretend" }, - { name = "pytest" }, - { name = "pytest-cov" }, + { name = "tqdm" }, ] [package.metadata] requires-dist = [ - { name = "build", marker = "extra == 'dev'" }, { name = "common", editable = "../common" }, - { name = "coverage", extras = ["toml"], marker = "extra == 'test'" }, - { name = "interrogate", marker = "extra == 'lint'" }, - { name = "mypy", marker = "extra == 'lint'", specifier = ">=1.0" }, - { name = "pretend", marker = "extra == 'test'" }, - { name = "pytest", marker = "extra == 'test'" }, - { name = "pytest-cov", marker = "extra == 'test'" }, + { name = "mypy", marker = "extra == 'dev'", specifier = "~=1.15.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = "~=8.3.4" }, { name = "redis", specifier = "~=5.2.1" }, - { name = "ruff", marker = "extra == 'lint'", specifier = "~=0.6.2" }, - { name = "seed-gen", extras = ["doc", "test", "lint"], marker = "extra == 'dev'" }, - { name = "twine", marker = "extra == 'dev'" }, - { name = "types-html5lib", marker = "extra == 'lint'" }, - { name = "types-requests", marker = "extra == 'lint'" }, - { name = "types-toml", marker = "extra == 'lint'" }, + { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.6.2" }, + { name = "tqdm", marker = "extra == 'dev'", specifier = "~=4.67.1" }, { name = "wasmtime", specifier = "~=29.0.0" }, ] @@ -1851,15 +1491,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/e4/592120713a314621c692211eba034d09becaf6bc8848fabc1dc2a54d8c16/SQLAlchemy-2.0.38-py3-none-any.whl", hash = "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753", size = 1896347 }, ] -[[package]] -name = "tabulate" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, -] - [[package]] name = "tenacity" version = "9.0.0" @@ -1899,56 +1530,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] -[[package]] -name = "twine" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "id" }, - { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, - { name = "packaging" }, - { name = "readme-renderer" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "rfc3986" }, - { name = "rich" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c8/a2/6df94fc5c8e2170d21d7134a565c3a8fb84f9797c1dd65a5976aaf714418/twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd", size = 168404 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/b6/74e927715a285743351233f33ea3c684528a0d374d2e43ff9ce9585b73fe/twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", size = 40791 }, -] - -[[package]] -name = "types-html5lib" -version = "1.1.11.20241018" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/9d/f6fbcc8246f5e46845b4f989c4e17e6fb3ce572f7065b185e515bf8a3be7/types-html5lib-1.1.11.20241018.tar.gz", hash = "sha256:98042555ff78d9e3a51c77c918b1041acbb7eb6c405408d8a9e150ff5beccafa", size = 11370 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/7c/f862b1dc31268ef10fe95b43dcdf216ba21a592fafa2d124445cd6b92e93/types_html5lib-1.1.11.20241018-py3-none-any.whl", hash = "sha256:3f1e064d9ed2c289001ae6392c84c93833abb0816165c6ff0abfc304a779f403", size = 17292 }, -] - -[[package]] -name = "types-requests" -version = "2.32.0.20241016" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fa/3c/4f2a430c01a22abd49a583b6b944173e39e7d01b688190a5618bd59a2e22/types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95", size = 18065 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/01/485b3026ff90e5190b5e24f1711522e06c79f4a56c8f4b95848ac072e20f/types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747", size = 15836 }, -] - -[[package]] -name = "types-toml" -version = "0.10.8.20240310" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 }, -] - [[package]] name = "typing-extensions" version = "4.12.2"