From 7990dc65757b4f780fc8151f4f7b6863bcc8a733 Mon Sep 17 00:00:00 2001 From: Raghav Pillai Date: Tue, 18 Nov 2025 18:04:34 -0800 Subject: [PATCH 1/5] Migrate gen-commit from Python to TypeScript - Remove Python CLI and helpers (gencommit.py, utils/config.py, utils/llm_wrapper.py) - Add Node/TypeScript CLI (src/cli.ts) implementing arg parsing, init, version, and commit flow - Introduce TypeScript utils: config.ts (zod validation), llm-wrapper.ts (ai SDK adapters), prompts.ts - Implement diff formatting, token estimation/truncation, retrying LLM calls, and safer git commit via spawnSync - Add config initialization template and improved config error handling (ConfigError) - Convert prompt templates to TS and wire LLM chat to generate structured commit messages - Remove Python package entry import (src/__init__.py) to reflect migration --- src/__init__.py | 1 - src/cli.ts | 341 +++++++++++++++++++++++++++ src/gencommit.py | 195 --------------- src/utils/config.py | 31 --- src/utils/config.ts | 81 +++++++ src/utils/llm-wrapper.ts | 110 +++++++++ src/utils/llm_wrapper.py | 72 ------ src/utils/{prompts.py => prompts.ts} | 20 +- 8 files changed, 542 insertions(+), 309 deletions(-) delete mode 100644 src/__init__.py create mode 100644 src/cli.ts delete mode 100644 src/gencommit.py delete mode 100644 src/utils/config.py create mode 100644 src/utils/config.ts create mode 100644 src/utils/llm-wrapper.ts delete mode 100644 src/utils/llm_wrapper.py rename src/utils/{prompts.py => prompts.ts} (74%) diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index f7b1b0e..0000000 --- a/src/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .gencommit import gencommit diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..8cb21ae --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,341 @@ +#!/usr/bin/env node + +import { execSync, spawnSync } from "node:child_process"; +import { existsSync, writeFileSync } from "node:fs"; +import { z } from "zod"; +import { + CONFIG_FILE, + type Config, + ConfigError, + readConfig, + readVersionFromPackageJson, +} from "./utils/config"; +import { chat } from "./utils/llm-wrapper"; +import { + COMMIT_PROMPT_SYSTEM, + COMMIT_PROMPT_WITH_DESCRIPTION, +} from "./utils/prompts"; + +const VERSION = readVersionFromPackageJson(); + +const CommitMessageSchema = z.object({ + commit_message: z + .string() + .describe("Brief descriptive commit message in no longer than 10 words"), + commit_description: z + .string() + .describe("Hyphenated bullet point list of changes"), +}); + +type CommitMessage = z.infer; + +interface Args { + m?: string; + d?: string; + init?: boolean; + version?: boolean; + unknownArgs: string[]; +} + +function parseArgs(): Args { + const args = process.argv.slice(2); + const parsed: Args = { unknownArgs: [] }; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (arg === "-m" && i + 1 < args.length) { + parsed.m = args[++i]; + } else if (arg === "-d" && i + 1 < args.length) { + parsed.d = args[++i]; + } else if (arg === "--init" || arg === "--initialize") { + parsed.init = true; + } else if (arg === "--version" || arg === "--v") { + parsed.version = true; + } else { + parsed.unknownArgs.push(arg); + } + } + + return parsed; +} + +function formatDiff(diffText: string, maxLineLength: number): string { + const fileChanges: Record< + string, + Array<{ type: "add" | "remove"; content: string }> + > = {}; + let currentFile = ""; + + for (const line of diffText.split("\n")) { + if (line.startsWith("diff --git")) { + const parts = line.split(" "); + currentFile = parts[parts.length - 1].replace(/^b\//, ""); + fileChanges[currentFile] = []; + } else if (line.startsWith("+") && !line.startsWith("+++")) { + if (currentFile) { + fileChanges[currentFile].push({ + type: "add", + content: line.slice(1, maxLineLength + 1).trim(), + }); + } + } else if (line.startsWith("-") && !line.startsWith("---")) { + if (currentFile) { + fileChanges[currentFile].push({ + type: "remove", + content: line.slice(1, maxLineLength + 1).trim(), + }); + } + } + } + + // Format into structured output + let formattedDiff = "### Git Changes Summary ###\n\n"; + + for (const [filename, changes] of Object.entries(fileChanges)) { + formattedDiff += `File: ${filename}\n`; + formattedDiff += "Changes:\n"; + + const limit = filename.endsWith(".lock") ? 30 : 2000; + const limitedChanges = changes.slice(0, limit); + + for (const change of limitedChanges) { + const prefix = change.type === "add" ? "+" : "-"; + formattedDiff += `${prefix} ${change.content}\n`; + } + + if (changes.length > limit) { + formattedDiff += `\n... (${changes.length - limit} additional changes truncated)\n`; + } + + formattedDiff += "\n"; + } + + return formattedDiff; +} + +// Simple tokenizer approximation (4 chars ≈ 1 token) +function estimateTokens(text: string): number { + return Math.ceil(text.length / 4); +} + +function truncateToTokenLimit(text: string, maxTokens: number): string { + const estimatedTokens = estimateTokens(text); + if (estimatedTokens <= maxTokens) { + return text; + } + + // Approximate character limit + const charLimit = maxTokens * 4; + return text.slice(0, charLimit); +} + +async function generateCommitMessage( + diffText: string, + maxTokensAllowed: number, + maxRetries = 3, +): Promise { + if (!diffText) { + throw new Error("No changes to commit"); + } + + const truncatedDiff = truncateToTokenLimit(diffText, maxTokensAllowed); + let lastError: Error | undefined; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + if (attempt > 1) { + console.log(`Retrying... (attempt ${attempt}/${maxRetries})`); + } + + const llmResponse = await chat( + COMMIT_PROMPT_SYSTEM(), + COMMIT_PROMPT_WITH_DESCRIPTION(truncatedDiff), + CommitMessageSchema, + ); + return llmResponse; + } catch (error) { + lastError = error as Error; + + if (attempt < maxRetries) { + const delay = Math.min(1000 * 2 ** (attempt - 1), 5000); + console.error(`Attempt ${attempt} failed, retrying in ${delay}ms...`); + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + } + + console.error("Error generating commit message:", lastError); + throw lastError; +} + +const DEFAULT_CONFIG_TEMPLATE = `MODEL=anthropic:claude-3-5-sonnet-20241022 +OPENAI_API_KEY= +ANTHROPIC_API_KEY= +GOOGLE_API_KEY= +MAX_LINE_LENGTH=300 +MAX_TOKENS_ALLOWED=30000`; + +async function initialize(): Promise { + const configFile = CONFIG_FILE; + + if (existsSync(configFile)) { + const readline = require("node:readline").createInterface({ + input: process.stdin, + output: process.stdout, + }); + + return new Promise((resolve) => { + readline.question( + `Config file already exists at ${configFile}. Overwrite? (Y/n): `, + (answer: string) => { + readline.close(); + const response = answer.trim().toLowerCase(); + + if (response !== "y" && response !== "") { + console.log("Initialization cancelled."); + resolve(false); + return; + } + + writeFileSync(configFile, DEFAULT_CONFIG_TEMPLATE); + console.log( + "gen-commit initialized successfully, wrote config to ~/.gen-commit", + ); + resolve(true); + }, + ); + }); + } + + writeFileSync(configFile, DEFAULT_CONFIG_TEMPLATE); + console.log( + "gen-commit initialized successfully, wrote config to ~/.gen-commit", + ); + return true; +} + +async function main() { + const args = parseArgs(); + + if (args.version) { + console.log(`gencommit ${VERSION}`); + process.exit(0); + } + + if (args.init) { + const success = await initialize(); + process.exit(success ? 0 : 1); + } + + let config: Config; + try { + config = readConfig(); + } catch (error) { + if (error instanceof ConfigError) { + console.error(error.message); + process.exit(1); + } + throw error; + } + + const MAX_LINE_LENGTH = parseInt(config.MAX_LINE_LENGTH || "300", 10); + const MAX_TOKENS_ALLOWED = parseInt(config.MAX_TOKENS_ALLOWED || "30000", 10); + + const hasMessage = args.m !== undefined; + const hasDescription = args.d !== undefined; + + // Check if there are any commits + let commitsExist = true; + try { + execSync("git rev-parse --verify HEAD", { + encoding: "utf-8", + stdio: "pipe", + }); + } catch { + commitsExist = false; + } + + // Check for unstaged changes if -a flag is present + if (args.unknownArgs.includes("-a")) { + try { + const unstagedChanges = execSync("git status --porcelain", { + encoding: "utf-8", + stdio: "pipe", + }).trim(); + if (!unstagedChanges) { + console.error("No changes detected."); + process.exit(1); + } + } catch (_error) { + console.error("Error checking for unstaged changes"); + process.exit(1); + } + } + + // Check for staged changes + try { + const stagedChanges = execSync("git diff --staged --name-only", { + encoding: "utf-8", + stdio: "pipe", + }).trim(); + if (!stagedChanges && !args.unknownArgs.includes("-a")) { + console.error("No changes staged for commit."); + process.exit(1); + } + } catch (_error) { + console.error("Error checking staged changes"); + process.exit(1); + } + + let commitMessage: string; + let commitDescription: string; + + if (commitsExist) { + try { + const diffOutput = execSync("git diff --staged", { + encoding: "utf-8", + stdio: "pipe", + }).trim(); + const formattedDiff = formatDiff(diffOutput, MAX_LINE_LENGTH); + const commitMessageObject = await generateCommitMessage( + formattedDiff, + MAX_TOKENS_ALLOWED, + ); + commitMessage = + hasMessage && args.m ? args.m : commitMessageObject.commit_message; + commitDescription = + hasDescription && args.d + ? args.d + : commitMessageObject.commit_description; + } catch (error) { + console.error("Error generating commit message:", error); + process.exit(1); + } + } else { + commitMessage = hasMessage && args.m ? args.m : "Initial commit"; + commitDescription = hasDescription && args.d ? args.d : ""; + } + + // Build git commit arguments (no shell escaping needed with spawnSync) + const gitArgs = ["commit", ...args.unknownArgs, "-m", commitMessage]; + if (commitDescription) { + gitArgs.push("-m", commitDescription); + } + + // Use spawnSync instead of execSync to avoid shell injection + const result = spawnSync("git", gitArgs, { + encoding: "utf-8", + stdio: "inherit", + }); + + if (result.error || result.status !== 0) { + console.error("Error executing git commit"); + process.exit(1); + } +} + +main().catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); diff --git a/src/gencommit.py b/src/gencommit.py deleted file mode 100644 index 2501a69..0000000 --- a/src/gencommit.py +++ /dev/null @@ -1,195 +0,0 @@ -import argparse -import os -import subprocess -import sys - -import tiktoken -from pydantic import BaseModel, Field - -from .utils.config import read_config, read_version_from_pyproject -from .utils.llm_wrapper import chat -from .utils.prompts import ( - COMMIT_PROMPT_SYSTEM, - COMMIT_PROMPT_WITH_DESCRIPTION, -) - -CONFIG: dict = read_config() -MAX_LINE_LENGTH: int = CONFIG.get("MAX_LINE_LENGTH", 300) -MAX_TOKENS_ALLOWED: int = CONFIG.get("MAX_TOKENS_ALLOWED", 30000) -VERSION: str = read_version_from_pyproject() - - -class CommitMessage(BaseModel): - thinking: str = Field( - ..., - description="A scratchpad to put your concise reasoning and chain of thought after looking at the diffs.", - ) - commit_message: str = Field( - ..., description="Brief descriptive commit message in no longer than 10 words" - ) - commit_description: str = Field( - ..., description="Hyphenated bullet point list of changes" - ) - - -def format_diff(diff_text: str) -> str: - file_changes: dict[str, list[tuple[str, str]]] = {} - current_file: str = "" - - for line in diff_text.split("\n"): - if line.startswith("diff --git"): - current_file = line.split()[-1].lstrip("b/") - file_changes[current_file] = [] - elif line.startswith("+") and not line.startswith("+++"): - if current_file: - file_changes[current_file].append( - ("add", line[1:MAX_LINE_LENGTH].strip()) - ) - elif line.startswith("-") and not line.startswith("---"): - if current_file: - file_changes[current_file].append( - ("remove", line[1:MAX_LINE_LENGTH].strip()) - ) - - # Format into a structured output - formatted_diff_text: str = "### Git Changes Summary ###\n\n" - for filename, changes in file_changes.items(): - formatted_diff_text += f"File: {filename}\n" - formatted_diff_text += "Changes:\n" - limit: int = 30 if filename.endswith(".lock") else 2000 - for change_type, content in changes[:limit]: - prefix = "+" if change_type == "add" else "-" - formatted_diff_text += f"{prefix} {content}\n" - if len(changes) > limit: - formatted_diff_text += ( - f"\n... ({len(changes) - limit} additional changes truncated)\n" - ) - formatted_diff_text += "\n" - - return formatted_diff_text - - -def generate_commit_message(diff_text: str) -> CommitMessage: - tokenizer: tiktoken.Encoding = tiktoken.encoding_for_model("gpt-4o") - if not diff_text: - return "No changes to commit", "" - - token_list: list[int] = tokenizer.encode(diff_text) - truncated_tokens: list[int] = token_list[:MAX_TOKENS_ALLOWED] - truncated_diff: str = tokenizer.decode(truncated_tokens) - - try: - llm_response: CommitMessage = chat( - system_prompt=COMMIT_PROMPT_SYSTEM(), - user_prompt=COMMIT_PROMPT_WITH_DESCRIPTION(truncated_diff), - response_model=CommitMessage, - ) - except Exception as e: - print(f"Error generating commit message: {e}") - raise e - return llm_response - - -def _initialize() -> bool: - home_dir: str = os.path.expanduser("~") - config_file: str = os.path.join(home_dir, ".gen-commit") - if os.path.exists(config_file): - user_input = ( - input(f"Config file already exists at {config_file}. Overwrite? (Y/n): ") - .strip() - .lower() - ) - if user_input not in ["y", ""]: - print("Initialization cancelled.") - return False - - with open(config_file, "w") as f: - f.write( - "MODEL=anthropic:claude-3-5-sonnet-20241022\nOPENAI_API_KEY=\nANTHROPIC_API_KEY=" - ) - return True - - -def gencommit(): - arg_parser: argparse.ArgumentParser = argparse.ArgumentParser() - arg_parser.add_argument("-m", type=str, help="Override commit message") - arg_parser.add_argument("-d", type=str, help="Override commit description") - arg_parser.add_argument( - "--init", "--initialize", action="store_true", help="Initialize gen-commit" - ) - arg_parser.add_argument( - "--version", "--v", action="version", version=f"%(prog)s {VERSION}" - ) - found_args, unknown_args = arg_parser.parse_known_args() - - has_message: bool = found_args.m is not None - has_description: bool = found_args.d is not None - - if found_args.init: - success: bool = _initialize() - if success: - print("gen-commit initialized successfully, wrote config to ~/.gen-commit") - sys.exit(0) - else: - print("gen-commit initialization failed.") - sys.exit(1) - - try: - # Check if there are any commits - subprocess.check_output( - ["git", "rev-parse", "--verify", "HEAD"], text=True - ).strip() - commits_exist = True - except subprocess.CalledProcessError: - commits_exist = False - - if "-a" in unknown_args: - # Check for unstaged changes - unstaged_changes = subprocess.check_output( - ["git", "status", "--porcelain"], - text=True, - ).strip() - if not unstaged_changes: - print("No changes detected.") - sys.exit(1) - - staged_changes = subprocess.check_output( - ["git", "diff", "--staged", "--name-only"], - text=True, - ).strip() - if not staged_changes and "-a" not in unknown_args: - print("No changes staged for commit.") - sys.exit(1) - - commit_message: str - commit_description: str - if commits_exist: - diff_output: str = subprocess.check_output( - ["git", "diff", "--staged"], text=True - ).strip() - formatted_diff: str = format_diff(diff_output) - commit_message_object: CommitMessage = generate_commit_message( - diff_text=formatted_diff - ) - commit_message = commit_message_object.commit_message - commit_description = commit_message_object.commit_description - else: - commit_message: str = found_args.m if has_message else "Initial commit" - commit_description: str = found_args.d if has_description else "" - - commit_message = commit_message.replace('"', '\\"') - commit_description = commit_description.replace('"', '\\"') - - commit_command: str - if commit_description: - commit_command = f"""git commit {" ".join(unknown_args)} -m "{commit_message}" -m "{commit_description}" """ - else: - commit_command = ( - f"""git commit {" ".join(unknown_args)} -m "{commit_message}" """ - ) - - subprocess.run(commit_command, shell=True, check=True) - - -if __name__ == "__main__": - gencommit() diff --git a/src/utils/config.py b/src/utils/config.py deleted file mode 100644 index 94c2c86..0000000 --- a/src/utils/config.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import sys - -import tomli - -HOME_DIR: str = os.path.expanduser("~") -CONFIG_FILE: str = os.path.join(HOME_DIR, ".gen-commit") - - -def read_config() -> dict: - if not os.path.exists(CONFIG_FILE): - print( - f"Config file not found at {CONFIG_FILE}. Please run `gencommit --init` to create a config file." - ) - sys.exit(1) - - config: dict[str, str] = {} - with open(CONFIG_FILE, "r") as f: - for line in f: - key, value = line.strip().split("=", 1) - config[key] = value - return config - - -def read_version_from_pyproject() -> str: - try: - with open("pyproject.toml", "rb") as f: - pyproject_data: dict[str, any] = tomli.load(f) - return pyproject_data["project"]["version"] - except (FileNotFoundError, KeyError): - return "0.0.0" # Default version if not found diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 0000000..3a6034c --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,81 @@ +import { existsSync, readFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; +import { z } from "zod"; + +const HOME_DIR = homedir(); +export const CONFIG_FILE = join(HOME_DIR, ".gen-commit"); + +const ConfigSchema = z.object({ + MODEL: z.string().min(1, "MODEL is required and cannot be empty"), + OPENAI_API_KEY: z.string().optional(), + ANTHROPIC_API_KEY: z.string().optional(), + GOOGLE_API_KEY: z.string().optional(), + MAX_LINE_LENGTH: z.string().optional(), + MAX_TOKENS_ALLOWED: z.string().optional(), +}); + +export type Config = z.infer; + +export class ConfigError extends Error { + constructor(message: string) { + super(message); + this.name = "ConfigError"; + } +} + +export function readConfig(): Config { + if (!existsSync(CONFIG_FILE)) { + throw new ConfigError( + `Config file not found at ${CONFIG_FILE}. Please run \`gencommit --init\` to create a config file.`, + ); + } + + const config: Record = {}; + const fileContent = readFileSync(CONFIG_FILE, "utf-8"); + + for (const line of fileContent.split("\n")) { + const trimmedLine = line.trim(); + if (trimmedLine && !trimmedLine.startsWith("#")) { + const separatorIndex = trimmedLine.indexOf("="); + if (separatorIndex !== -1) { + const key = trimmedLine.slice(0, separatorIndex).trim(); + const value = trimmedLine.slice(separatorIndex + 1).trim(); + if (key) { + // Normalize empty strings to undefined for optional fields + config[key] = value || undefined; + } + } + } + } + + try { + return ConfigSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const issues = error.issues.map((issue) => issue.message).join(", "); + throw new ConfigError(`Invalid config file at ${CONFIG_FILE}: ${issues}`); + } + throw error; + } +} + +export function readVersionFromPackageJson(): string { + try { + const packageJsonPath = join(process.cwd(), "package.json"); + if (existsSync(packageJsonPath)) { + const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); + return packageJson.version || "0.0.0"; + } + } catch (_error) { + // Fallback: try to import from package.json directly + try { + // For when running as installed package + const packageJson = require("../../package.json"); + return packageJson.version || "0.0.0"; + } catch { + return "0.0.0"; + } + } + return "0.0.0"; +} diff --git a/src/utils/llm-wrapper.ts b/src/utils/llm-wrapper.ts new file mode 100644 index 0000000..c20ed91 --- /dev/null +++ b/src/utils/llm-wrapper.ts @@ -0,0 +1,110 @@ +import { anthropic } from "@ai-sdk/anthropic"; +import { google } from "@ai-sdk/google"; +import { openai } from "@ai-sdk/openai"; +import { generateObject } from "ai"; +import type { z } from "zod"; +import { type Config, readConfig } from "./config"; + +export interface ModelParams { + provider: string; + modelName: string; + apiKey: string; +} + +export function getModelParams(): ModelParams { + const config: Config = readConfig(); + + if (!config.MODEL) { + throw new Error("MODEL not found in config"); + } + + const [provider, modelName] = config.MODEL.toLowerCase().split(":", 2); + + if (!provider || !modelName) { + throw new Error('Invalid MODEL format. Expected "provider:model-name"'); + } + + let apiKey: string | undefined; + + switch (provider) { + case "openai": + apiKey = config.OPENAI_API_KEY; + if (!apiKey) { + throw new Error("OPENAI_API_KEY not found in config"); + } + break; + case "anthropic": + apiKey = config.ANTHROPIC_API_KEY; + if (!apiKey) { + throw new Error("ANTHROPIC_API_KEY not found in config"); + } + break; + case "google": + apiKey = config.GOOGLE_API_KEY; + if (!apiKey) { + throw new Error("GOOGLE_API_KEY not found in config"); + } + break; + default: + throw new Error(`Invalid provider: ${provider}`); + } + + return { provider, modelName, apiKey }; +} + +export function getModel(params: ModelParams) { + const { provider, modelName, apiKey } = params; + + switch (provider) { + case "openai": + return openai(modelName, { apiKey }); + case "anthropic": + return anthropic(modelName, { apiKey }); + case "google": + return google(modelName, { apiKey }); + default: + throw new Error(`Unsupported provider: ${provider}`); + } +} + +export interface ChatOptions { + temperature?: number; + maxTokens?: number; + timeout?: number; +} + +export async function chat( + systemPrompt: string, + userPrompt: string, + schema: T, + options: ChatOptions = {}, +): Promise> { + const { temperature = 0, maxTokens = 1024, timeout = 60000 } = options; + + const params = getModelParams(); + const model = getModel(params); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + const result = await generateObject({ + model, + schema, + system: systemPrompt, + prompt: userPrompt, + temperature, + maxTokens, + abortSignal: controller.signal, + }); + + return result.object as z.infer; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + throw new Error( + `Failed to generate object using ${params.provider}:${params.modelName}: ${errorMessage}`, + ); + } finally { + clearTimeout(timeoutId); + } +} diff --git a/src/utils/llm_wrapper.py b/src/utils/llm_wrapper.py deleted file mode 100644 index 9631f57..0000000 --- a/src/utils/llm_wrapper.py +++ /dev/null @@ -1,72 +0,0 @@ -from openai import OpenAI -from openai.types.chat import ChatCompletion -from pydantic import BaseModel - -from .config import read_config - - -def chat( - system_prompt: str, - user_prompt: str, - response_model: BaseModel, -) -> BaseModel: - provider_params, request_params = _get_params() - - client: OpenAI = OpenAI(**provider_params) - response: ChatCompletion = client.chat.completions.parse( - **request_params, - messages=[ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_prompt}, - ], - max_tokens=1024, - temperature=0, - response_format=response_model, - ) - return response.choices[0].message.parsed - - -def _get_params() -> tuple[dict, dict]: - config: dict = read_config() - model: str = config.get("MODEL") - if not model: - raise ValueError("MODEL not found in config") - - provider, model_name = model.lower().split(":", 1) - - provider_params: dict = {} - request_params: dict = {} - - if provider == "openai": - api_key: str = config.get("OPENAI_API_KEY") - if not api_key: - raise ValueError("OPENAI_API_KEY not found in environment") - provider_params["api_key"] = api_key - elif provider == "anthropic": - api_key: str = config.get("ANTHROPIC_API_KEY") - if not api_key: - raise ValueError("ANTHROPIC_API_KEY not found in environment") - provider_params["api_key"] = api_key - provider_params["base_url"] = "https://api.anthropic.com/v1/" - elif provider == "ollama": - api_key: str = config.get("GOOGLE_API_KEY") - if not api_key: - raise ValueError("GOOGLE_API_KEY not found in environment") - provider_params["api_key"] = api_key - provider_params["base_url"] = ( - "https://generativelanguage.googleapis.com/v1beta/openai/" - ) - else: - raise ValueError(f"Invalid provider: {provider}") - - reasoning_models: list[str] = [ - "gemini-2.5-flash-lite", - "gemini-2.5-flash", - "gemini-2.5-pro", - ] - if model_name in reasoning_models: - request_params["reasoning_effort"] = "none" - - request_params["model"] = model_name - - return provider_params, request_params diff --git a/src/utils/prompts.py b/src/utils/prompts.ts similarity index 74% rename from src/utils/prompts.py rename to src/utils/prompts.ts index d344511..a389ae0 100644 --- a/src/utils/prompts.py +++ b/src/utils/prompts.ts @@ -1,7 +1,6 @@ -def COMMIT_PROMPT_SYSTEM() -> str: - return """ -You are senior engineer who is looking at your git diff and trying to write a commit message. \ -Your task is to create a descriptive commit message based on this diff. +export function COMMIT_PROMPT_SYSTEM(): string { + return ` +You are senior engineer who is looking at your git diff and trying to write a commit message. Your task is to create a descriptive commit message based on this diff. @@ -14,16 +13,17 @@ def COMMIT_PROMPT_SYSTEM() -> str: - For large changes, summarize the overall impact rather than listing every small modification. - You can use incomplete sentences or phrases to get your point across. -""".strip() +`.trim(); +} - -def COMMIT_PROMPT_WITH_DESCRIPTION(diffs: str) -> str: - return f""" +export function COMMIT_PROMPT_WITH_DESCRIPTION(diffs: string): string { + return ` Given the following code changes: -{diffs} +${diffs} Please generate a concise and informative git commit message and description based on these changes. -""".strip() +`.trim(); +} From 946a1629a1b4a5b46a05df71895451f6dc56d8c8 Mon Sep 17 00:00:00 2001 From: Raghav Pillai Date: Tue, 18 Nov 2025 18:20:17 -0800 Subject: [PATCH 2/5] Migrate project from Python to TypeScript/Bun - Replace Python project configuration (pyproject, requirements, uv.lock) with Node/Bun setup (package.json, bun.lock) - Introduce TypeScript build config (tsconfig.json) and Biome linter settings (biome.json) - Add .npmignore and overhaul .gitignore for JS/TS, Bun, IDE, and OS files - Add Bun lockfile and dependencies; remove many Python-specific files and scripts - Convert and update src code: TypeScript CLI tweaks (token/truncation, debug timing, config defaults, git commit execution commented) - Update LLM wrapper to new SDK factory APIs, set abort timeout and reasoningEffort - Remove legacy Python tests and utility scripts; clean up build/upload shell scripts --- .gitignore | 190 +++---------- .npmignore | 19 ++ bin/build.sh | 1 - bin/upload.sh | 1 - biome.json | 35 +++ bun.lock | 79 ++++++ package.json | 60 ++++ pyproject.toml | 34 --- requirements.txt | 24 -- src/cli.ts | 38 +-- src/utils/config.ts | 5 +- src/utils/llm-wrapper.ts | 38 ++- tests/generic.py | 307 -------------------- tsconfig.json | 19 ++ utils/build.sh | 1 - utils/convert_requirements.py | 61 ---- utils/upload.sh | 1 - uv.lock | 510 ---------------------------------- 18 files changed, 284 insertions(+), 1139 deletions(-) create mode 100644 .npmignore delete mode 100644 bin/build.sh delete mode 100644 bin/upload.sh create mode 100644 biome.json create mode 100644 bun.lock create mode 100644 package.json delete mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 tests/generic.py create mode 100644 tsconfig.json delete mode 100644 utils/build.sh delete mode 100644 utils/convert_requirements.py delete mode 100644 utils/upload.sh delete mode 100644 uv.lock diff --git a/.gitignore b/.gitignore index 2294344..712e677 100644 --- a/.gitignore +++ b/.gitignore @@ -1,165 +1,43 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ +# Node.js / TypeScript +node_modules/ dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py +*.tsbuildinfo +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version +# Build outputs +*.js.map -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments +# Environment variables .env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject +.env.local +.env.*.local -# mkdocs documentation -/site +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json +# OS +.DS_Store +Thumbs.db -# Pyre type checker -.pyre/ +# Bun +bun.lockb -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +# Legacy Python files (if any remain) +__pycache__/ +*.py[cod] +*$py.class +*.egg-info/ +.venv/ +venv/ +.pytest_cache/ -# System -.DS_Store \ No newline at end of file +# Logs +*.log diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..b68ba2f --- /dev/null +++ b/.npmignore @@ -0,0 +1,19 @@ +src/ +tests/ +bin/ +assets/ +*.py +*.pyc +__pycache__/ +.pytest_cache/ +*.egg-info/ +pyproject.toml +requirements.txt +uv.lock +tsconfig.json +*.ts +!*.d.ts +node_modules/ +.DS_Store +.vscode/ +.idea/ diff --git a/bin/build.sh b/bin/build.sh deleted file mode 100644 index d1c3285..0000000 --- a/bin/build.sh +++ /dev/null @@ -1 +0,0 @@ -python3 -m build \ No newline at end of file diff --git a/bin/upload.sh b/bin/upload.sh deleted file mode 100644 index 42215c6..0000000 --- a/bin/upload.sh +++ /dev/null @@ -1 +0,0 @@ -twine upload dist/* \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..0a0e1b3 --- /dev/null +++ b/biome.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.6/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": ["**", "!!**/dist"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..cd7a538 --- /dev/null +++ b/bun.lock @@ -0,0 +1,79 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "gen-commit", + "dependencies": { + "@ai-sdk/anthropic": "^2.0.45", + "@ai-sdk/google": "^2.0.36", + "@ai-sdk/openai": "^2.0.68", + "ai": "^5.0.95", + "zod": "^4.1.12", + }, + "devDependencies": { + "@biomejs/biome": "^2.3.6", + "@types/bun": "latest", + "typescript": "^5.7.0", + }, + }, + }, + "packages": { + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.45", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Ipv62vavDCmrV/oE/lXehL9FzwQuZOnnlhPEftWizx464Wb6lvnBTJx8uhmEYruFSzOWTI95Z33ncZ4tA8E6RQ=="], + + "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.11", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.3" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-B0Vt2Xv88Lo9rg861Oyzq/SdTmT4LiqdjkpOxpSPpNk8Ih5TXTgyDAsV/qW14N6asPdK1YI5PosFLnVzfK5LrA=="], + + "@ai-sdk/google": ["@ai-sdk/google@2.0.36", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-1Z2tV6A4PobK+zTKDd9NqqlDPh0lwF8yf8+FcOLCjHzyDB498iyiBFwKQ6lhQT8YYhY60jVsmsSvSTm+TOyazw=="], + + "@ai-sdk/openai": ["@ai-sdk/openai@2.0.68", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qUSLFkqgUoFArzBwttu0KWVAZYjbsdZGOklSJXpfZ2nDC61yseHxtcnuG8u6tqKnGXDh4eakEgREDWU2sRht7A=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + + "@biomejs/biome": ["@biomejs/biome@2.3.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.6", "@biomejs/cli-darwin-x64": "2.3.6", "@biomejs/cli-linux-arm64": "2.3.6", "@biomejs/cli-linux-arm64-musl": "2.3.6", "@biomejs/cli-linux-x64": "2.3.6", "@biomejs/cli-linux-x64-musl": "2.3.6", "@biomejs/cli-win32-arm64": "2.3.6", "@biomejs/cli-win32-x64": "2.3.6" }, "bin": { "biome": "bin/biome" } }, "sha512-oqUhWyU6tae0MFsr/7iLe++QWRg+6jtUhlx9/0GmCWDYFFrK366sBLamNM7D9Y+c7YSynUFKr8lpEp1r6Sk7eA=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P4JWE5d8UayBxYe197QJwyW4ZHp0B+zvRIGCusOm1WbxmlhpAQA1zEqQuunHgSIzvyEEp4TVxiKGXNFZPg7r9Q=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-I4rTebj+F/L9K93IU7yTFs8nQ6EhaCOivxduRha4w4WEZK80yoZ8OAdR1F33m4yJ/NfUuTUbP/Wjs+vKjlCoWA=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-JjYy83eVBnvuINZiqyFO7xx72v8Srh4hsgaacSBCjC22DwM6+ZvnX1/fj8/SBiLuUOfZ8YhU2pfq2Dzakeyg1A=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-oK1NpIXIixbJ/4Tcx40cwiieqah6rRUtMGOHDeK2ToT7yUFVEvXUGRKqH0O4hqZ9tW8TcXNZKfgRH6xrsjVtGg=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-ZjPXzy5yN9wusIoX+8Zp4p6cL8r0NzJCXg/4r1KLVveIPXd2jKVlqZ6ZyzEq385WwU3OX5KOwQYLQsOc788waQ=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-QvxB8GHQeaO4FCtwJpJjCgJkbHBbWxRHUxQlod+xeaYE6gtJdSkYkuxdKAQUZEOIsec+PeaDAhW9xjzYbwmOFA=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-YM7hLHpwjdt8R7+O2zS1Vo2cKgqEeptiXB1tWW1rgjN5LlpZovBVKtg7zfwfRrFx3i08aNZThYpTcowpTlczug=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.6", "", { "os": "win32", "cpu": "x64" }, "sha512-psgNEYgMAobY5h+QHRBVR9xvg2KocFuBKm6axZWB/aD12NWhQjiVFQUjV6wMXhlH4iT0Q9c3yK5JFRiDC/rzHA=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="], + + "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + + "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="], + + "@vercel/oidc": ["@vercel/oidc@3.0.3", "", {}, "sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg=="], + + "ai": ["ai@5.0.95", "", { "dependencies": { "@ai-sdk/gateway": "2.0.11", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-dsvFdYMeGP08zuUQkhKO1UMMXMCb+nro9ZmDdwaAkkTlCGkP3u1S+xaRUDNayu/c0KVkiTtfEroPG//U+kvXzg=="], + + "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1959d15 --- /dev/null +++ b/package.json @@ -0,0 +1,60 @@ +{ + "name": "gen-commit", + "version": "2.0.0", + "description": "Auto-generate git commit messages using AI", + "type": "module", + "bin": { + "gencommit": "./dist/cli.js" + }, + "files": [ + "dist/**/*", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "bun run lint && bun build src/cli.ts --outdir dist --target node --format esm --minify --sourcemap=external && chmod +x dist/cli.js", + "dev": "bun --watch src/cli.ts", + "prepublishOnly": "bun run build", + "test": "bun test", + "link": "npm link", + "lint": "biome check src/", + "lint:fix": "biome check --write src/", + "format": "biome format --write src/" + }, + "keywords": [ + "git", + "commit", + "ai", + "llm", + "cli", + "automation" + ], + "author": { + "name": "Raghav Pillai", + "email": "me@raghav.sh" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/raghavpillai/gen-commit.git" + }, + "bugs": { + "url": "https://github.com/raghavpillai/gen-commit/issues" + }, + "homepage": "https://github.com/raghavpillai/gen-commit#readme", + "engines": { + "node": ">=18.0.0" + }, + "dependencies": { + "@ai-sdk/anthropic": "^2.0.45", + "@ai-sdk/google": "^2.0.36", + "@ai-sdk/openai": "^2.0.68", + "ai": "^5.0.95", + "zod": "^4.1.12" + }, + "devDependencies": { + "@biomejs/biome": "^2.3.6", + "@types/bun": "latest", + "typescript": "^5.7.0" + } +} diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 9b49db4..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,34 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "gen-commit" -version = "1.0.0" -dependencies = [ - "openai", - "tiktoken>=0.9.0", - "tomli", -] -requires-python = ">=3.11" -authors = [ - { name="Raghav Pillai", email="me@raghav.sh" }, -] -description = "Auto-generate git commit messages" -readme = "README.md" -license = { file="LICENSE" } -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] - -[project.urls] -Homepage = "https://github.com/raghavpillai/gen-commit" -Issues = "https://github.com/raghavpillai/gen-commit/issues" - -[project.scripts] -gencommit = "src.gencommit:gencommit" - -[tool.hatch.build.targets.wheel] -packages = ["src"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7257da1..0000000 --- a/requirements.txt +++ /dev/null @@ -1,24 +0,0 @@ -# This file was autogenerated by uv via the following command: -annotated-types==0.7.0 -anyio==4.9.0 -certifi==2025.7.14 -charset-normalizer==3.4.2 -colorama==0.4.6 ; platform_system == 'Windows' -distro==1.9.0 -h11==0.16.0 -httpcore==1.0.9 -httpx==0.28.1 -idna==3.10 -jiter==0.10.0 -openai==1.97.1 -pydantic==2.11.7 -pydantic-core==2.33.2 -regex==2024.11.6 -requests==2.32.4 -sniffio==1.3.1 -tiktoken==0.9.0 -tomli==2.2.1 -tqdm==4.67.1 -typing-extensions==4.14.1 -typing-inspection==0.4.1 -urllib3==2.5.0 diff --git a/src/cli.ts b/src/cli.ts index 8cb21ae..bc31c4c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -89,7 +89,6 @@ function formatDiff(diffText: string, maxLineLength: number): string { } } - // Format into structured output let formattedDiff = "### Git Changes Summary ###\n\n"; for (const [filename, changes] of Object.entries(fileChanges)) { @@ -114,7 +113,6 @@ function formatDiff(diffText: string, maxLineLength: number): string { return formattedDiff; } -// Simple tokenizer approximation (4 chars ≈ 1 token) function estimateTokens(text: string): number { return Math.ceil(text.length / 4); } @@ -125,9 +123,11 @@ function truncateToTokenLimit(text: string, maxTokens: number): string { return text; } - // Approximate character limit const charLimit = maxTokens * 4; - return text.slice(0, charLimit); + const halfLimit = Math.floor(charLimit / 2); + const start = text.slice(0, halfLimit); + const end = text.slice(-halfLimit); + return `${start}\n\n... [truncated] ...\n\n${end}`; } async function generateCommitMessage( @@ -148,11 +148,18 @@ async function generateCommitMessage( console.log(`Retrying... (attempt ${attempt}/${maxRetries})`); } + const startTime = Date.now(); + const llmResponse = await chat( COMMIT_PROMPT_SYSTEM(), COMMIT_PROMPT_WITH_DESCRIPTION(truncatedDiff), CommitMessageSchema, ); + + const endTime = Date.now(); + const duration = ((endTime - startTime) / 1000).toFixed(2); + console.log(`[DEBUG] AI generation completed in ${duration}s`); + return llmResponse; } catch (error) { lastError = error as Error; @@ -169,7 +176,7 @@ async function generateCommitMessage( throw lastError; } -const DEFAULT_CONFIG_TEMPLATE = `MODEL=anthropic:claude-3-5-sonnet-20241022 +const DEFAULT_CONFIG_TEMPLATE = `MODEL=openai:gpt-5-mini OPENAI_API_KEY= ANTHROPIC_API_KEY= GOOGLE_API_KEY= @@ -245,7 +252,6 @@ async function main() { const hasMessage = args.m !== undefined; const hasDescription = args.d !== undefined; - // Check if there are any commits let commitsExist = true; try { execSync("git rev-parse --verify HEAD", { @@ -256,7 +262,6 @@ async function main() { commitsExist = false; } - // Check for unstaged changes if -a flag is present if (args.unknownArgs.includes("-a")) { try { const unstagedChanges = execSync("git status --porcelain", { @@ -273,7 +278,6 @@ async function main() { } } - // Check for staged changes try { const stagedChanges = execSync("git diff --staged --name-only", { encoding: "utf-8", @@ -317,22 +321,20 @@ async function main() { commitDescription = hasDescription && args.d ? args.d : ""; } - // Build git commit arguments (no shell escaping needed with spawnSync) const gitArgs = ["commit", ...args.unknownArgs, "-m", commitMessage]; if (commitDescription) { gitArgs.push("-m", commitDescription); } - // Use spawnSync instead of execSync to avoid shell injection - const result = spawnSync("git", gitArgs, { - encoding: "utf-8", - stdio: "inherit", - }); + // const result = spawnSync("git", gitArgs, { + // encoding: "utf-8", + // stdio: "inherit", + // }); - if (result.error || result.status !== 0) { - console.error("Error executing git commit"); - process.exit(1); - } + // if (result.error || result.status !== 0) { + // console.error("Error executing git commit"); + // process.exit(1); + // } } main().catch((error) => { diff --git a/src/utils/config.ts b/src/utils/config.ts index 3a6034c..66c8755 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -42,8 +42,7 @@ export function readConfig(): Config { const key = trimmedLine.slice(0, separatorIndex).trim(); const value = trimmedLine.slice(separatorIndex + 1).trim(); if (key) { - // Normalize empty strings to undefined for optional fields - config[key] = value || undefined; + config[key] = value; } } } @@ -68,9 +67,7 @@ export function readVersionFromPackageJson(): string { return packageJson.version || "0.0.0"; } } catch (_error) { - // Fallback: try to import from package.json directly try { - // For when running as installed package const packageJson = require("../../package.json"); return packageJson.version || "0.0.0"; } catch { diff --git a/src/utils/llm-wrapper.ts b/src/utils/llm-wrapper.ts index c20ed91..c763109 100644 --- a/src/utils/llm-wrapper.ts +++ b/src/utils/llm-wrapper.ts @@ -1,6 +1,6 @@ -import { anthropic } from "@ai-sdk/anthropic"; -import { google } from "@ai-sdk/google"; -import { openai } from "@ai-sdk/openai"; +import { createAnthropic } from "@ai-sdk/anthropic"; +import { createGoogleGenerativeAI } from "@ai-sdk/google"; +import { createOpenAI } from "@ai-sdk/openai"; import { generateObject } from "ai"; import type { z } from "zod"; import { type Config, readConfig } from "./config"; @@ -56,36 +56,33 @@ export function getModel(params: ModelParams) { const { provider, modelName, apiKey } = params; switch (provider) { - case "openai": - return openai(modelName, { apiKey }); - case "anthropic": - return anthropic(modelName, { apiKey }); - case "google": - return google(modelName, { apiKey }); + case "openai": { + const openai = createOpenAI({ apiKey }); + return openai(modelName); + } + case "anthropic": { + const anthropic = createAnthropic({ apiKey }); + return anthropic(modelName); + } + case "google": { + const google = createGoogleGenerativeAI({ apiKey }); + return google(modelName); + } default: throw new Error(`Unsupported provider: ${provider}`); } } -export interface ChatOptions { - temperature?: number; - maxTokens?: number; - timeout?: number; -} - export async function chat( systemPrompt: string, userPrompt: string, schema: T, - options: ChatOptions = {}, ): Promise> { - const { temperature = 0, maxTokens = 1024, timeout = 60000 } = options; - const params = getModelParams(); const model = getModel(params); const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); + const timeoutId = setTimeout(() => controller.abort(), 60000); try { const result = await generateObject({ @@ -93,9 +90,8 @@ export async function chat( schema, system: systemPrompt, prompt: userPrompt, - temperature, - maxTokens, abortSignal: controller.signal, + reasoningEffort: "minimal", }); return result.object as z.infer; diff --git a/tests/generic.py b/tests/generic.py deleted file mode 100644 index d3c59c0..0000000 --- a/tests/generic.py +++ /dev/null @@ -1,307 +0,0 @@ -import time - -from src.gencommit import CommitMessage, format_diff, generate_commit_message - - -class FakeGitDiffs: - """Collection of fake git diffs for testing gencommit performance.""" - - @staticmethod - def small_python_change() -> str: - """Small Python file modification.""" - return """diff --git a/src/main.py b/src/main.py -index 1234567..abcdefg 100644 ---- a/src/main.py -+++ b/src/main.py -@@ -10,7 +10,8 @@ def calculate_total(items): - total = 0 - for item in items: -- total += item.price -+ # Apply discount if available -+ total += item.price * (1 - item.discount) - return total - - def process_order(order):""" - - @staticmethod - def medium_javascript_refactor() -> str: - """Medium-sized JavaScript refactoring.""" - return """diff --git a/frontend/components/UserProfile.js b/frontend/components/UserProfile.js -index abcd123..efgh456 100644 ---- a/frontend/components/UserProfile.js -+++ b/frontend/components/UserProfile.js -@@ -1,15 +1,22 @@ --import React from 'react'; -+import React, { useState, useEffect } from 'react'; -+import { fetchUserData } from '../api/users'; - --const UserProfile = ({ user }) => { -+const UserProfile = ({ userId }) => { -+ const [user, setUser] = useState(null); -+ const [loading, setLoading] = useState(true); -+ -+ useEffect(() => { -+ const loadUser = async () => { -+ try { -+ const userData = await fetchUserData(userId); -+ setUser(userData); -+ } catch (error) { -+ console.error('Failed to load user:', error); -+ } finally { -+ setLoading(false); -+ } -+ }; -+ loadUser(); -+ }, [userId]); -+ -+ if (loading) return
Loading...
; - return ( -
-

{user.name}

--

{user.email}

-+

Email: {user.email}

-+

Joined: {new Date(user.createdAt).toLocaleDateString()}

-
- ); - };""" - - @staticmethod - def large_database_migration() -> str: - """Large database migration with multiple tables.""" - return """diff --git a/migrations/001_add_user_preferences.sql b/migrations/001_add_user_preferences.sql -new file mode 100644 -index 0000000..1234567 ---- /dev/null -+++ b/migrations/001_add_user_preferences.sql -@@ -0,0 +1,45 @@ -+-- Add user preferences table -+CREATE TABLE user_preferences ( -+ id SERIAL PRIMARY KEY, -+ user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, -+ theme VARCHAR(20) DEFAULT 'light', -+ language VARCHAR(10) DEFAULT 'en', -+ timezone VARCHAR(50) DEFAULT 'UTC', -+ email_notifications BOOLEAN DEFAULT true, -+ push_notifications BOOLEAN DEFAULT true, -+ marketing_emails BOOLEAN DEFAULT false, -+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -+); -+ -+-- Add indexes for better query performance -+CREATE INDEX idx_user_preferences_user_id ON user_preferences(user_id); -+CREATE INDEX idx_user_preferences_theme ON user_preferences(theme); -+CREATE INDEX idx_user_preferences_language ON user_preferences(language); -+ -+-- Add trigger to update updated_at timestamp -+CREATE OR REPLACE FUNCTION update_updated_at_column() -+RETURNS TRIGGER AS $$ -+BEGIN -+ NEW.updated_at = CURRENT_TIMESTAMP; -+ RETURN NEW; -+END; -+$$ language 'plpgsql'; -+ -+CREATE TRIGGER update_user_preferences_updated_at -+ BEFORE UPDATE ON user_preferences -+ FOR EACH ROW -+ EXECUTE FUNCTION update_updated_at_column(); -+ -+-- Insert default preferences for existing users -+INSERT INTO user_preferences (user_id) -+SELECT id FROM users WHERE id NOT IN (SELECT user_id FROM user_preferences); -+ -+-- Add new columns to users table -+ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP; -+ALTER TABLE users ADD COLUMN login_count INTEGER DEFAULT 0; -+ALTER TABLE users ADD COLUMN is_premium BOOLEAN DEFAULT false; -+ -+-- Create index on new columns -+CREATE INDEX idx_users_last_login ON users(last_login_at); -+CREATE INDEX idx_users_is_premium ON users(is_premium);""" - - @staticmethod - def config_file_changes() -> str: - """Configuration file updates.""" - return """diff --git a/config/database.yml b/config/database.yml -index abc123..def456 100644 ---- a/config/database.yml -+++ b/config/database.yml -@@ -1,8 +1,12 @@ - development: - adapter: postgresql - encoding: unicode -- database: myapp_development -+ database: myapp_dev - pool: 5 -- username: postgres -- password: password -+ username: <%= ENV['DB_USERNAME'] %> -+ password: <%= ENV['DB_PASSWORD'] %> - host: localhost -+ port: 5432 -+ timeout: 5000 -+ prepared_statements: false -+ advisory_locks: true - -diff --git a/config/redis.conf b/config/redis.conf -index 789abc..012def 100644 ---- a/config/redis.conf -+++ b/config/redis.conf -@@ -50,10 +50,15 @@ save 900 1 - save 300 10 - save 60 10000 - --maxmemory 2gb -+maxmemory 4gb - maxmemory-policy allkeys-lru -+maxmemory-samples 5 - - # Network - bind 127.0.0.1 --port 6379 -+port <%= ENV['REDIS_PORT'] || 6379 %> - timeout 0 -+tcp-keepalive 300 -+tcp-backlog 511 -+ -+# Security -+requirepass <%= ENV['REDIS_PASSWORD'] %>""" - - @staticmethod - def multi_file_feature() -> str: - """Multiple files changed for a new feature.""" - return """diff --git a/src/api/auth.py b/src/api/auth.py -new file mode 100644 -index 0000000..1234567 ---- /dev/null -+++ b/src/api/auth.py -@@ -0,0 +1,25 @@ -+from flask import Blueprint, request, jsonify -+from werkzeug.security import check_password_hash -+from models.user import User -+from utils.jwt_handler import generate_token -+ -+auth_bp = Blueprint('auth', __name__) -+ -+@auth_bp.route('/login', methods=['POST']) -+def login(): -+ data = request.get_json() -+ email = data.get('email') -+ password = data.get('password') -+ -+ user = User.query.filter_by(email=email).first() -+ -+ if user and check_password_hash(user.password_hash, password): -+ token = generate_token(user.id) -+ return jsonify({ -+ 'success': True, -+ 'token': token, -+ 'user': user.to_dict() -+ }) -+ -+ return jsonify({'success': False, 'message': 'Invalid credentials'}), 401 - -diff --git a/src/models/user.py b/src/models/user.py -index abc123..def456 100644 ---- a/src/models/user.py -+++ b/src/models/user.py -@@ -1,4 +1,5 @@ - from sqlalchemy import Column, Integer, String, DateTime, Boolean -+from werkzeug.security import generate_password_hash - from database import db - import datetime - -@@ -8,8 +9,18 @@ class User(db.Model): - email = Column(String(120), unique=True, nullable=False) - password_hash = Column(String(128), nullable=False) - is_active = Column(Boolean, default=True) -+ is_verified = Column(Boolean, default=False) - created_at = Column(DateTime, default=datetime.datetime.utcnow) -+ last_login = Column(DateTime) - - def __repr__(self): - return f'' -+ -+ def set_password(self, password): -+ self.password_hash = generate_password_hash(password) -+ -+ def to_dict(self): -+ return { -+ 'id': self.id, -+ 'email': self.email, -+ 'is_active': self.is_active -+ } - -diff --git a/tests/test_auth.py b/tests/test_auth.py -new file mode 100644 -index 0000000..7890abc ---- /dev/null -+++ b/tests/test_auth.py -@@ -0,0 +1,20 @@ -+import pytest -+from app import create_app -+from models.user import User -+ -+@pytest.fixture -+def client(): -+ app = create_app('testing') -+ with app.test_client() as client: -+ yield client -+ -+def test_login_success(client): -+ # Create test user -+ user = User(email='test@example.com') -+ user.set_password('password123') -+ -+ response = client.post('/api/auth/login', json={ -+ 'email': 'test@example.com', -+ 'password': 'password123' -+ }) -+ assert response.status_code == 200""" - - -def benchmark_generate_commit_message(): - """Benchmark generate_commit_message performance with different diff sizes.""" - - test_diffs: list[tuple[str, str]] = [ - ("Small Python Change", FakeGitDiffs.small_python_change()), - ("Medium JS Refactor", FakeGitDiffs.medium_javascript_refactor()), - ("Large DB Migration", FakeGitDiffs.large_database_migration()), - ("Config File Changes", FakeGitDiffs.config_file_changes()), - ("Multi-file Feature", FakeGitDiffs.multi_file_feature()), - ] - - for test_name, diff_content in test_diffs: - print(f"\nTesting: {test_name}") - print("-" * 40) - - formatted_diff: str = format_diff(diff_content) - - start_time = time.time() - try: - commit_message: CommitMessage = generate_commit_message(formatted_diff) - generation_time = time.time() - start_time - - print(f"Duration: {generation_time:.2f}s") - print(f"Thinking: {commit_message.thinking}") - print(f"Commit Message: {commit_message.commit_message}") - print(f"Description: {commit_message.commit_description}") - - except Exception as e: - generation_time = time.time() - start_time - print(f"Duration: {generation_time:.2f}s") - print(f"Error: {e}") - - -if __name__ == "__main__": - try: - benchmark_generate_commit_message() - - print("\nAll tests completed successfully!") - - except Exception as e: - print(f"\nTest failed: {e}") - import traceback - - traceback.print_exc() diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a84cd23 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext"], + "moduleResolution": "bundler", + "resolveJsonModule": true, + "allowJs": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "types": ["bun-types"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests"] +} diff --git a/utils/build.sh b/utils/build.sh deleted file mode 100644 index d1c3285..0000000 --- a/utils/build.sh +++ /dev/null @@ -1 +0,0 @@ -python3 -m build \ No newline at end of file diff --git a/utils/convert_requirements.py b/utils/convert_requirements.py deleted file mode 100644 index 50e5ed6..0000000 --- a/utils/convert_requirements.py +++ /dev/null @@ -1,61 +0,0 @@ -import requests - - -def generate_resource_blocks(requirements_file="requirements.txt"): - pypi_base_url = "https://pypi.org/pypi" - - resource_blocks = "" - - with open(requirements_file, "r") as file: - for line in file: - line = line.strip() - - if line.startswith("-e") or line.startswith("#") or line == "": - continue - - try: - pkg_name, version = line.split("==") - except ValueError: - print(f"Skipping invalid line: {line}") - continue - - pkg_info_url = f"{pypi_base_url}/{pkg_name}/{version}/json" - response = requests.get(pkg_info_url) - - if response.status_code != 200: - print(f"Failed to fetch package info for {pkg_name}=={version}") - continue - - pkg_info = response.json() - # Select the tar.gz distribution URL - selected_url = next( - ( - url_info - for url_info in pkg_info["urls"] - if url_info["url"].endswith(".tar.gz") - ), - None, - ) - - if not selected_url: - print(f"No .tar.gz distribution found for {pkg_name}=={version}") - continue - - download_url = selected_url["url"] - sha256 = selected_url["digests"]["sha256"] - - resource_block = ( - f'resource "{pkg_name}" do\n' - f' url "{download_url}"\n' - f' sha256 "{sha256}"\n' - "end\n\n" - ) - - resource_blocks += resource_block - - return resource_blocks - - -if __name__ == "__main__": - resources_text = generate_resource_blocks() - print(resources_text) diff --git a/utils/upload.sh b/utils/upload.sh deleted file mode 100644 index 40b91ef..0000000 --- a/utils/upload.sh +++ /dev/null @@ -1 +0,0 @@ -twine upload dist/* \ No newline at end of file diff --git a/uv.lock b/uv.lock deleted file mode 100644 index b93442f..0000000 --- a/uv.lock +++ /dev/null @@ -1,510 +0,0 @@ -version = 1 -requires-python = ">=3.11" - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, -] - -[[package]] -name = "anyio" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, -] - -[[package]] -name = "certifi" -version = "2025.7.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722 }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, -] - -[[package]] -name = "gen-commit" -version = "1.0.0" -source = { editable = "." } -dependencies = [ - { name = "openai" }, - { name = "tiktoken" }, - { name = "tomli" }, -] - -[package.metadata] -requires-dist = [ - { name = "openai" }, - { name = "tiktoken", specifier = ">=0.9.0" }, - { name = "tomli" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } -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 = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, -] - -[[package]] -name = "jiter" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978", size = 317473 }, - { url = "https://files.pythonhosted.org/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc", size = 321971 }, - { url = "https://files.pythonhosted.org/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d", size = 345574 }, - { url = "https://files.pythonhosted.org/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2", size = 371028 }, - { url = "https://files.pythonhosted.org/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61", size = 491083 }, - { url = "https://files.pythonhosted.org/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db", size = 388821 }, - { url = "https://files.pythonhosted.org/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5", size = 352174 }, - { url = "https://files.pythonhosted.org/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606", size = 391869 }, - { url = "https://files.pythonhosted.org/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605", size = 523741 }, - { url = "https://files.pythonhosted.org/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5", size = 514527 }, - { url = "https://files.pythonhosted.org/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7", size = 210765 }, - { url = "https://files.pythonhosted.org/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812", size = 209234 }, - { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262 }, - { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124 }, - { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330 }, - { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670 }, - { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057 }, - { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372 }, - { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038 }, - { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538 }, - { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557 }, - { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202 }, - { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781 }, - { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176 }, - { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 }, - { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 }, - { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 }, - { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 }, - { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 }, - { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 }, - { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 }, - { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 }, - { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 }, - { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 }, - { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 }, - { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 }, - { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 }, - { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 }, - { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 }, - { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 }, - { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 }, - { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 }, - { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 }, - { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 }, - { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 }, - { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 }, - { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 }, - { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 }, - { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 }, - { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 }, - { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, -] - -[[package]] -name = "openai" -version = "1.97.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/57/1c471f6b3efb879d26686d31582997615e969f3bb4458111c9705e56332e/openai-1.97.1.tar.gz", hash = "sha256:a744b27ae624e3d4135225da9b1c89c107a2a7e5bc4c93e5b7b5214772ce7a4e", size = 494267 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/35/412a0e9c3f0d37c94ed764b8ac7adae2d834dbd20e69f6aca582118e0f55/openai-1.97.1-py3-none-any.whl", hash = "sha256:4e96bbdf672ec3d44968c9ea39d2c375891db1acc1794668d8149d5fa6000606", size = 764380 }, -] - -[[package]] -name = "pydantic" -version = "2.11.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, -] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 }, - { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 }, - { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 }, - { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 }, - { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 }, - { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 }, - { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 }, - { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 }, - { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 }, - { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 }, - { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 }, - { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 }, - { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 }, - { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 }, - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, - { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 }, - { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 }, - { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 }, - { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 }, - { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 }, - { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 }, - { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 }, - { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 }, - { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, -] - -[[package]] -name = "regex" -version = "2024.11.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, - { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, - { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, - { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, - { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, - { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, - { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, - { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, - { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, - { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, - { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, - { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, - { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, - { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, - { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, - { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, - { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, - { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, - { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, - { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, - { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, - { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, - { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, - { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, - { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, - { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, - { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, - { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, - { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, - { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, - { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, - { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, - { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, - { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, - { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, - { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, - { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, - { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, - { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, - { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, - { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, - { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, - { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, - { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, - { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, -] - -[[package]] -name = "requests" -version = "2.32.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 }, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, -] - -[[package]] -name = "tiktoken" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/cf/756fedf6981e82897f2d570dd25fa597eb3f4459068ae0572d7e888cfd6f/tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d", size = 35991 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/ae/4613a59a2a48e761c5161237fc850eb470b4bb93696db89da51b79a871f1/tiktoken-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f32cc56168eac4851109e9b5d327637f15fd662aa30dd79f964b7c39fbadd26e", size = 1065987 }, - { url = "https://files.pythonhosted.org/packages/3f/86/55d9d1f5b5a7e1164d0f1538a85529b5fcba2b105f92db3622e5d7de6522/tiktoken-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45556bc41241e5294063508caf901bf92ba52d8ef9222023f83d2483a3055348", size = 1009155 }, - { url = "https://files.pythonhosted.org/packages/03/58/01fb6240df083b7c1916d1dcb024e2b761213c95d576e9f780dfb5625a76/tiktoken-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03935988a91d6d3216e2ec7c645afbb3d870b37bcb67ada1943ec48678e7ee33", size = 1142898 }, - { url = "https://files.pythonhosted.org/packages/b1/73/41591c525680cd460a6becf56c9b17468d3711b1df242c53d2c7b2183d16/tiktoken-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3d80aad8d2c6b9238fc1a5524542087c52b860b10cbf952429ffb714bc1136", size = 1197535 }, - { url = "https://files.pythonhosted.org/packages/7d/7c/1069f25521c8f01a1a182f362e5c8e0337907fae91b368b7da9c3e39b810/tiktoken-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b2a21133be05dc116b1d0372af051cd2c6aa1d2188250c9b553f9fa49301b336", size = 1259548 }, - { url = "https://files.pythonhosted.org/packages/6f/07/c67ad1724b8e14e2b4c8cca04b15da158733ac60136879131db05dda7c30/tiktoken-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:11a20e67fdf58b0e2dea7b8654a288e481bb4fc0289d3ad21291f8d0849915fb", size = 893895 }, - { url = "https://files.pythonhosted.org/packages/cf/e5/21ff33ecfa2101c1bb0f9b6df750553bd873b7fb532ce2cb276ff40b197f/tiktoken-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e88f121c1c22b726649ce67c089b90ddda8b9662545a8aeb03cfef15967ddd03", size = 1065073 }, - { url = "https://files.pythonhosted.org/packages/8e/03/a95e7b4863ee9ceec1c55983e4cc9558bcfd8f4f80e19c4f8a99642f697d/tiktoken-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6600660f2f72369acb13a57fb3e212434ed38b045fd8cc6cdd74947b4b5d210", size = 1008075 }, - { url = "https://files.pythonhosted.org/packages/40/10/1305bb02a561595088235a513ec73e50b32e74364fef4de519da69bc8010/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e811743b5dfa74f4b227927ed86cbc57cad4df859cb3b643be797914e41794", size = 1140754 }, - { url = "https://files.pythonhosted.org/packages/1b/40/da42522018ca496432ffd02793c3a72a739ac04c3794a4914570c9bb2925/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99376e1370d59bcf6935c933cb9ba64adc29033b7e73f5f7569f3aad86552b22", size = 1196678 }, - { url = "https://files.pythonhosted.org/packages/5c/41/1e59dddaae270ba20187ceb8aa52c75b24ffc09f547233991d5fd822838b/tiktoken-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:badb947c32739fb6ddde173e14885fb3de4d32ab9d8c591cbd013c22b4c31dd2", size = 1259283 }, - { url = "https://files.pythonhosted.org/packages/5b/64/b16003419a1d7728d0d8c0d56a4c24325e7b10a21a9dd1fc0f7115c02f0a/tiktoken-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:5a62d7a25225bafed786a524c1b9f0910a1128f4232615bf3f8257a73aaa3b16", size = 894897 }, - { url = "https://files.pythonhosted.org/packages/7a/11/09d936d37f49f4f494ffe660af44acd2d99eb2429d60a57c71318af214e0/tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb", size = 1064919 }, - { url = "https://files.pythonhosted.org/packages/80/0e/f38ba35713edb8d4197ae602e80837d574244ced7fb1b6070b31c29816e0/tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63", size = 1007877 }, - { url = "https://files.pythonhosted.org/packages/fe/82/9197f77421e2a01373e27a79dd36efdd99e6b4115746ecc553318ecafbf0/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01", size = 1140095 }, - { url = "https://files.pythonhosted.org/packages/f2/bb/4513da71cac187383541facd0291c4572b03ec23c561de5811781bbd988f/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139", size = 1195649 }, - { url = "https://files.pythonhosted.org/packages/fa/5c/74e4c137530dd8504e97e3a41729b1103a4ac29036cbfd3250b11fd29451/tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a", size = 1258465 }, - { url = "https://files.pythonhosted.org/packages/de/a8/8f499c179ec900783ffe133e9aab10044481679bb9aad78436d239eee716/tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95", size = 894669 }, -] - -[[package]] -name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, -] - -[[package]] -name = "tqdm" -version = "4.67.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } -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 = "typing-extensions" -version = "4.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906 }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, -] - -[[package]] -name = "urllib3" -version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, -] From 025592cd65af9aa07c231b4e5b8ebfa1c4175e2d Mon Sep 17 00:00:00 2001 From: Raghav Pillai Date: Tue, 18 Nov 2025 18:33:29 -0800 Subject: [PATCH 3/5] Simplify diff handling and improve LLM wrapper options - Remove custom diff formatting and return raw git diff text - Support unstaged full-diff via '-a' flag (use git diff HEAD) - Remove MAX_LINE_LENGTH config/env usage and related parsing - Re-enable git commit execution error handling (spawnSync check) - Add provider-specific reasoningEffort options for OpenAI/Anthropic/Google in LLM wrapper - Update prompts and config schema to reflect removed MAX_LINE_LENGTH setting --- src/cli.ts | 85 ++++++++-------------------------------- src/utils/config.ts | 1 - src/utils/llm-wrapper.ts | 12 +++++- src/utils/prompts.ts | 2 +- 4 files changed, 28 insertions(+), 72 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index bc31c4c..40cf5ca 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -60,57 +60,8 @@ function parseArgs(): Args { return parsed; } -function formatDiff(diffText: string, maxLineLength: number): string { - const fileChanges: Record< - string, - Array<{ type: "add" | "remove"; content: string }> - > = {}; - let currentFile = ""; - - for (const line of diffText.split("\n")) { - if (line.startsWith("diff --git")) { - const parts = line.split(" "); - currentFile = parts[parts.length - 1].replace(/^b\//, ""); - fileChanges[currentFile] = []; - } else if (line.startsWith("+") && !line.startsWith("+++")) { - if (currentFile) { - fileChanges[currentFile].push({ - type: "add", - content: line.slice(1, maxLineLength + 1).trim(), - }); - } - } else if (line.startsWith("-") && !line.startsWith("---")) { - if (currentFile) { - fileChanges[currentFile].push({ - type: "remove", - content: line.slice(1, maxLineLength + 1).trim(), - }); - } - } - } - - let formattedDiff = "### Git Changes Summary ###\n\n"; - - for (const [filename, changes] of Object.entries(fileChanges)) { - formattedDiff += `File: ${filename}\n`; - formattedDiff += "Changes:\n"; - - const limit = filename.endsWith(".lock") ? 30 : 2000; - const limitedChanges = changes.slice(0, limit); - - for (const change of limitedChanges) { - const prefix = change.type === "add" ? "+" : "-"; - formattedDiff += `${prefix} ${change.content}\n`; - } - - if (changes.length > limit) { - formattedDiff += `\n... (${changes.length - limit} additional changes truncated)\n`; - } - - formattedDiff += "\n"; - } - - return formattedDiff; +function formatDiff(diffText: string): string { + return diffText; } function estimateTokens(text: string): number { @@ -148,18 +99,12 @@ async function generateCommitMessage( console.log(`Retrying... (attempt ${attempt}/${maxRetries})`); } - const startTime = Date.now(); - const llmResponse = await chat( COMMIT_PROMPT_SYSTEM(), COMMIT_PROMPT_WITH_DESCRIPTION(truncatedDiff), CommitMessageSchema, ); - const endTime = Date.now(); - const duration = ((endTime - startTime) / 1000).toFixed(2); - console.log(`[DEBUG] AI generation completed in ${duration}s`); - return llmResponse; } catch (error) { lastError = error as Error; @@ -180,7 +125,6 @@ const DEFAULT_CONFIG_TEMPLATE = `MODEL=openai:gpt-5-mini OPENAI_API_KEY= ANTHROPIC_API_KEY= GOOGLE_API_KEY= -MAX_LINE_LENGTH=300 MAX_TOKENS_ALLOWED=30000`; async function initialize(): Promise { @@ -246,7 +190,6 @@ async function main() { throw error; } - const MAX_LINE_LENGTH = parseInt(config.MAX_LINE_LENGTH || "300", 10); const MAX_TOKENS_ALLOWED = parseInt(config.MAX_TOKENS_ALLOWED || "30000", 10); const hasMessage = args.m !== undefined; @@ -297,15 +240,19 @@ async function main() { if (commitsExist) { try { - const diffOutput = execSync("git diff --staged", { + const diffCommand = args.unknownArgs.includes("-a") + ? "git diff HEAD" + : "git diff --staged"; + const diffOutput = execSync(diffCommand, { encoding: "utf-8", stdio: "pipe", }).trim(); - const formattedDiff = formatDiff(diffOutput, MAX_LINE_LENGTH); + const formattedDiff = formatDiff(diffOutput); const commitMessageObject = await generateCommitMessage( formattedDiff, MAX_TOKENS_ALLOWED, ); + commitMessage = hasMessage && args.m ? args.m : commitMessageObject.commit_message; commitDescription = @@ -326,15 +273,15 @@ async function main() { gitArgs.push("-m", commitDescription); } - // const result = spawnSync("git", gitArgs, { - // encoding: "utf-8", - // stdio: "inherit", - // }); + const result = spawnSync("git", gitArgs, { + encoding: "utf-8", + stdio: "inherit", + }); - // if (result.error || result.status !== 0) { - // console.error("Error executing git commit"); - // process.exit(1); - // } + if (result.error || result.status !== 0) { + console.error("Error executing git commit"); + process.exit(1); + } } main().catch((error) => { diff --git a/src/utils/config.ts b/src/utils/config.ts index 66c8755..f62767a 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -11,7 +11,6 @@ const ConfigSchema = z.object({ OPENAI_API_KEY: z.string().optional(), ANTHROPIC_API_KEY: z.string().optional(), GOOGLE_API_KEY: z.string().optional(), - MAX_LINE_LENGTH: z.string().optional(), MAX_TOKENS_ALLOWED: z.string().optional(), }); diff --git a/src/utils/llm-wrapper.ts b/src/utils/llm-wrapper.ts index c763109..a8349ae 100644 --- a/src/utils/llm-wrapper.ts +++ b/src/utils/llm-wrapper.ts @@ -91,7 +91,17 @@ export async function chat( system: systemPrompt, prompt: userPrompt, abortSignal: controller.signal, - reasoningEffort: "minimal", + providerOptions: { + openai: { + reasoningEffort: "minimal", + }, + anthropic: { + reasoningEffort: "minimal", + }, + google: { + reasoningEffort: "minimal", + }, + }, }); return result.object as z.infer; diff --git a/src/utils/prompts.ts b/src/utils/prompts.ts index a389ae0..2df4609 100644 --- a/src/utils/prompts.ts +++ b/src/utils/prompts.ts @@ -7,7 +7,7 @@ You are senior engineer who is looking at your git diff and trying to write a co Follow these guidelines: - You should first think and skim over the diffs to find what's important. -- The commit message should be brief and descriptive, no longer than 10 words. Focus on the functional changes. +- The commit message should be brief and descriptive, no longer than 10 words. Focus on the functional and high-level changes. - The commit description should be a bullet point list of the main changes. Bullet points should start with '-'. - Be concise and get your point across, as functional as possible. - For large changes, summarize the overall impact rather than listing every small modification. From 4e07b2146c3cb90bbba288d81f014166a2828d7f Mon Sep 17 00:00:00 2001 From: Raghav Pillai Date: Wed, 19 Nov 2025 12:44:00 -0800 Subject: [PATCH 4/5] CI, package rename, publish config, README updates - Add GitHub Actions CI workflow with test, security, and publish-test jobs - Remove .npmignore to include files via package.json files field - Rename package to @raghavp/gen-commit and bump version to 2.0.2 - Add publishConfig public access and adjust files list - Update README installation instructions and model examples - Remove Python install instructions and related config notes --- .github/workflows/ci.yml | 97 ++++++++++++++++++++++++++++++++++++++++ .npmignore | 19 -------- README.md | 32 +------------ package.json | 9 ++-- 4 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .npmignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b530b85 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,97 @@ +name: CI + +on: + push: + branches: [main, feat/ts-rewrite] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Test (Node v${{ matrix.node-version }}) + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18, 20, 22] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run linting + run: bun run lint + + - name: Build project + run: bun run build + + - name: Test CLI installation + run: | + npm pack + npm install -g gen-commit-*.tgz + gencommit --version + + security: + name: Security Audit + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run security audit + run: bun audit + + publish-test: + name: Test NPM Publish + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build project + run: bun run build + + - name: Test package creation + run: npm pack --dry-run diff --git a/.npmignore b/.npmignore deleted file mode 100644 index b68ba2f..0000000 --- a/.npmignore +++ /dev/null @@ -1,19 +0,0 @@ -src/ -tests/ -bin/ -assets/ -*.py -*.pyc -__pycache__/ -.pytest_cache/ -*.egg-info/ -pyproject.toml -requirements.txt -uv.lock -tsconfig.json -*.ts -!*.d.ts -node_modules/ -.DS_Store -.vscode/ -.idea/ diff --git a/README.md b/README.md index 8ccad80..0447d6f 100644 --- a/README.md +++ b/README.md @@ -44,35 +44,8 @@ alias gc="gencommit" ## Installation -### Recommended: Homebrew (macOS and Linux) - -```bash -brew tap raghavpillai/gen-commit -brew install gen-commit -``` - -### Python - -#### Prerequisites: - -- Python 3.11+ - -Install the gen-commit package using pip: - -```bash -pip install gen-commit -``` - -If you get - -```bash -error: externally-managed-environment -``` - -This means Python now doesn't want you to install this as a system-wide package. You can install it using `pipx` or `venv`, or be lazy and install it via: - ```bash -pip3 install gen-commit --break-system-packages +npm install -g @raghavp/gen-commit ``` ### Initialization @@ -88,7 +61,7 @@ Go to `~/.gen-commit` and add your OpenAI, Anthropic, or Google API key. ### Configuration ```bash -MODEL= +MODEL= OPENAI_API_KEY= ANTHROPIC_API_KEY= GOOGLE_API_KEY= @@ -97,6 +70,5 @@ GOOGLE_API_KEY= OPTIONAL: ``` -MAX_LINE_LENGTH= MAX_TOKENS_ALLOWED= ``` diff --git a/package.json b/package.json index 1959d15..028a7a3 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { - "name": "gen-commit", - "version": "2.0.0", + "name": "@raghavp/gen-commit", + "version": "2.0.2", "description": "Auto-generate git commit messages using AI", "type": "module", "bin": { "gencommit": "./dist/cli.js" }, "files": [ - "dist/**/*", + "dist", "README.md", "LICENSE" ], @@ -34,6 +34,9 @@ "email": "me@raghav.sh" }, "license": "MIT", + "publishConfig": { + "access": "public" + }, "repository": { "type": "git", "url": "https://github.com/raghavpillai/gen-commit.git" From a463202ee99d35e2d741c1a7a39d446b7a46d996 Mon Sep 17 00:00:00 2001 From: Raghav Pillai Date: Wed, 19 Nov 2025 12:45:25 -0800 Subject: [PATCH 5/5] Update CI to install namespaced package tarball - change npm global install pattern in CI from gen-commit-*.tgz to raghavp-gen-commit-*.tgz - ensure CI tests install and run the correct packaged CLI tarball --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b530b85..f4ba62a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: - name: Test CLI installation run: | npm pack - npm install -g gen-commit-*.tgz + npm install -g raghavp-gen-commit-*.tgz gencommit --version security: