diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1d2f29ef1fc..8188eadf1db 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,21 @@ repos: - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + rev: 9.0.0a3 hooks: - id: isort args: ["--profile", "black"] - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 26.3.1 hooks: - id: black args: ["--line-length", "100", "--preview"] - repo: https://github.com/pycqa/flake8 - rev: 7.1.0 + rev: 7.3.0 hooks: - id: flake8 args: ["--show-source"] - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.4.2 hooks: - id: codespell args: ["--skip", "cecli/website/docs/languages.md"] diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index c2ddaafc29d..eb8c34a6b63 100755 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -292,9 +292,7 @@ def simple_namespace_to_dict(obj): if not dry and "CECLI_DOCKER" not in os.environ: logger.warning("Warning: Benchmarking runs unvetted code. Run in a docker container.") - logger.warning( - "Set CECLI_DOCKER in the environment to by-pass this check at your own risk." - ) + logger.warning("Set CECLI_DOCKER in the environment to bypass this check at your own risk.") return # Check dirs exist diff --git a/cecli/__init__.py b/cecli/__init__.py index 1527c4f728f..4e43758df83 100644 --- a/cecli/__init__.py +++ b/cecli/__init__.py @@ -1,6 +1,6 @@ from packaging import version -__version__ = "0.99.3.dev" +__version__ = "0.99.6.dev" safe_version = __version__ try: diff --git a/cecli/coders/agent_coder.py b/cecli/coders/agent_coder.py index a3cf86b629e..a24714dff1b 100644 --- a/cecli/coders/agent_coder.py +++ b/cecli/coders/agent_coder.py @@ -582,6 +582,7 @@ def format_chat_chunks(self): # Add post-message context blocks (priority 250 - between CUR and REMINDER) ConversationService.get_chunks(self).add_post_message_context_blocks() + ConversationService.get_chunks(self).add_randomized_cta() return ConversationService.get_manager(self).get_messages_dict() diff --git a/cecli/coders/base_coder.py b/cecli/coders/base_coder.py index 0dd6203e298..0b7f847d436 100755 --- a/cecli/coders/base_coder.py +++ b/cecli/coders/base_coder.py @@ -1131,8 +1131,9 @@ def _include_in_map(abs_path): "other_files": other_files, "mentioned_fnames": mentioned_fnames, "all_abs_files": all_abs_files, - "read_only_count": len(set(self.abs_read_only_fnames)) + len( - set(self.abs_read_only_stubs_fnames) + "read_only_count": ( + len(set(self.abs_read_only_fnames)) + + len(set(self.abs_read_only_stubs_fnames)) ), } ) diff --git a/cecli/commands/load_mcp.py b/cecli/commands/load_mcp.py index 110a55dce5c..eb1e6d2e402 100644 --- a/cecli/commands/load_mcp.py +++ b/cecli/commands/load_mcp.py @@ -6,7 +6,7 @@ class LoadMcpCommand(BaseCommand): NORM_NAME = "load-mcp" - DESCRIPTION = "Load a MCP server by name" + DESCRIPTION = "Load MCP server(s) by name, or use '*' to load all enabled servers" @classmethod async def execute(cls, io, coder, args, **kwargs): @@ -21,17 +21,34 @@ async def execute(cls, io, coder, args, **kwargs): server_names = args.strip().split() results = [] - for server_name in server_names: - server = coder.mcp_manager.get_server(server_name) - if server is None: - results.append(f"MCP server {server_name} does not exist.") - continue - did_connect = await coder.mcp_manager.connect_server(server.name) - if did_connect: - results.append(f"Loaded server: {server_name}") - else: - results.append(f"Unable to load server: {server_name}") + # Handle '*' wildcard to load all servers enabled by default + if server_names == ["*"]: + for server in coder.mcp_manager.servers: + if server in coder.mcp_manager.connected_servers: + results.append(f"Server already loaded: {server.name}") + continue + auto_connect = server.config.get("enabled", True) + if not auto_connect: + results.append(f"Skipping server (not enabled by default): {server.name}") + continue + did_connect = await coder.mcp_manager.connect_server(server.name) + if did_connect: + results.append(f"Loaded server: {server.name}") + else: + results.append(f"Unable to load server: {server.name}") + else: + for server_name in server_names: + server = coder.mcp_manager.get_server(server_name) + if server is None: + results.append(f"MCP server {server_name} does not exist.") + continue + + did_connect = await coder.mcp_manager.connect_server(server.name) + if did_connect: + results.append(f"Loaded server: {server_name}") + else: + results.append(f"Unable to load server: {server_name}") try: return format_command_result(io, cls.NORM_NAME, "\n".join(results)) @@ -67,8 +84,8 @@ def get_help(cls) -> str: help_text = super().get_help() help_text += "\nUsage:\n" help_text += " /load-mcp ... # Load one or more mcps by name\n" + help_text += " /load-mcp * # Load all mcps enabled by default\n" help_text += "\nExamples:\n" help_text += " /load-mcp context7 # Load the context7 mcp\n" help_text += " /load-mcp github context7 # Load both github and context7 mcps\n" - help_text += "\nThis command loads one or more MCP servers by name.\n" - return help_text + help_text += " /load-mcp * # Load all mcps enabled by default\n" diff --git a/cecli/commands/remove_mcp.py b/cecli/commands/remove_mcp.py index f12caaf7b25..2239d7ba883 100644 --- a/cecli/commands/remove_mcp.py +++ b/cecli/commands/remove_mcp.py @@ -6,7 +6,7 @@ class RemoveMcpCommand(BaseCommand): NORM_NAME = "remove-mcp" - DESCRIPTION = "Remove a MCP server by name" + DESCRIPTION = "Remove a MCP server by name, or use '*' to remove all" @classmethod async def execute(cls, io, coder, args, **kwargs): @@ -21,12 +21,23 @@ async def execute(cls, io, coder, args, **kwargs): server_names = args.strip().split() results = [] - for server_name in server_names: - was_disconnected = await coder.mcp_manager.disconnect_server(server_name) - if was_disconnected: - results.append(f"Removed server: {server_name}") + + # Handle '*' wildcard to disconnect all servers + if server_names == ["*"]: + connected = [s for s in coder.mcp_manager.servers if s.is_connected] + if not connected: + results.append("No MCP servers connected, nothing to remove.") else: - results.append(f"Unable to remove server: {server_name}") + for server in connected: + await coder.mcp_manager.disconnect_server(server.name) + results.append(f"Removed server: {server.name}") + else: + for server_name in server_names: + was_disconnected = await coder.mcp_manager.disconnect_server(server_name) + if was_disconnected: + results.append(f"Removed server: {server_name}") + else: + results.append(f"Unable to remove server: {server_name}") try: return format_command_result(io, cls.NORM_NAME, "\n".join(results)) @@ -59,7 +70,8 @@ def get_help(cls) -> str: help_text = super().get_help() help_text += "\nUsage:\n" help_text += " /remove-mcp ... # Remove one or more mcps by name\n" + help_text += " /remove-mcp * # Remove all connected mcps\n" help_text += "\nExamples:\n" help_text += " /remove-mcp context7 # Remove the context7 mcp\n" help_text += " /remove-mcp github context7 # Remove both github and context7 mcps\n" - help_text += "\nThis command removes one or more MCP servers by name.\n" + help_text += " /remove-mcp * # Remove all connected mcps\n" diff --git a/cecli/helpers/conversation/integration.py b/cecli/helpers/conversation/integration.py index 5519cb1deec..785d2e215d8 100644 --- a/cecli/helpers/conversation/integration.py +++ b/cecli/helpers/conversation/integration.py @@ -1,4 +1,5 @@ import json +import random import weakref from typing import Any, Dict, List from uuid import UUID @@ -93,7 +94,9 @@ def add_system_messages(self) -> None: ): msg = dict( role="user", - content=coder.fmt_system_prompt(coder.gpt_prompts.system_reminder), + content=self._shuffle_reminders( + coder.fmt_system_prompt(coder.gpt_prompts.system_reminder) + ), ) ConversationService.get_manager(coder).add_message( message_dict=msg, @@ -103,6 +106,62 @@ def add_system_messages(self) -> None: mark_for_delete=0, ) + def add_randomized_cta(self) -> None: + coder = self.get_coder() + if not coder: + return + + message = random.choice( + [ + "Given the above, please call any tools necessary to make progress on your task", + ( + "Based on the information provided, please execute the appropriate tools to" + " move the task forward." + ), + "With this context in mind, please proceed with your work.", + ( + "In light of the above, please utilize the required tools to continue with this" + " request." + ), + ( + "Continue making progress. If you have reached the goal, summarize the results." + " Otherwise, call the next necessary tool." + ), + ( + "Please use the proper tools to fulfill the next steps of this task based on" + " the current data." + ), + ( + "You’ve got what you need, please invoke the right tools to keep making" + " progress towards our goal." + ), + ( + "Considering what we've established, please use the available tools to complete" + " the current objective." + ), + ( + "Given this information, please use the available tools to proceed with the" + " assignment." + ), + "Please take the next logical steps to make headway on this task.", + ] + ) + + msg = dict( + role="user", + content="\n\n" + message, + ) + + ConversationService.get_manager(coder).add_message( + message_dict=msg, + tag=MessageTag.REMINDER, + hash_key=("main", "randomized_cta"), + force=True, + mark_for_delete=0, + promotion=2 * ConversationService.get_manager(coder).DEFAULT_TAG_PROMOTION_VALUE, + mark_for_demotion=1, + ) + def cleanup_files(self) -> None: """ Clean up ConversationFiles and remove corresponding messages from ConversationManager @@ -903,6 +962,35 @@ def add_post_message_context_blocks(self) -> None: force=True, ) + def _shuffle_reminders(self, content: str) -> str: + """ + If the string is a critical_reminders block, shuffle all bulleted points + to prevent the model from developing 'boilerplate blindness.' + """ + if not content.strip().startswith(''): + return content + + lines = content.splitlines() + + # 1. Identify indices of lines starting with a hyphen (and the content itself) + # We use strip() to handle indentation within the XML block + list_info = [(i, line) for i, line in enumerate(lines) if line.strip().startswith("-")] + + if not list_info: + return content + + # 2. Extract and shuffle the list items + indices = [item[0] for item in list_info] + bullet_contents = [item[1] for item in list_info] + random.shuffle(bullet_contents) + + # 3. Reconstruct the block by placing shuffled items back into the original indices + new_lines = list(lines) + for index, shuffled_text in zip(indices, bullet_contents): + new_lines[index] = shuffled_text + + return "\n".join(new_lines) + def debug_print_conversation_state(self) -> None: """ Print debug information about conversation state. diff --git a/cecli/helpers/grep_ast/__init__.py b/cecli/helpers/grep_ast/__init__.py new file mode 100644 index 00000000000..970031a51af --- /dev/null +++ b/cecli/helpers/grep_ast/__init__.py @@ -0,0 +1,4 @@ +# noqa: F401 + +from .grep_ast import TreeContext # noqa +from .parsers import filename_to_lang # noqa diff --git a/cecli/helpers/grep_ast/dump.py b/cecli/helpers/grep_ast/dump.py new file mode 100644 index 00000000000..2c8bf31c2d8 --- /dev/null +++ b/cecli/helpers/grep_ast/dump.py @@ -0,0 +1,29 @@ +import json +import traceback + + +def cvt(s): + if isinstance(s, str): + return s + try: + return json.dumps(s, indent=4) + except TypeError: + return str(s) + + +def dump(*vals): + # http://docs.python.org/library/traceback.html + stack = traceback.extract_stack() + vars = stack[-2][3] + + # strip away the call to dump() + vars = "(".join(vars.split("(")[1:]) + vars = ")".join(vars.split(")")[:-1]) + + vals = [cvt(v) for v in vals] + has_newline = sum(1 for v in vals if "\n" in v) + if has_newline: + print("%s:" % vars) + print(", ".join(vals)) + else: + print("%s:" % vars, ", ".join(vals)) diff --git a/cecli/helpers/grep_ast/grep_ast.py b/cecli/helpers/grep_ast/grep_ast.py new file mode 100644 index 00000000000..678a694a899 --- /dev/null +++ b/cecli/helpers/grep_ast/grep_ast.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python + +import re + +from .dump import dump # noqa: F401 +from .parsers import filename_to_lang +from .tsl import get_parser + + +class TreeContext: + def __init__( + self, + filename, + code, + color=False, + verbose=False, + line_number=False, + parent_context=True, + child_context=True, + last_line=True, + margin=3, + mark_lois=True, + header_max=10, + show_top_of_file_parent_scope=True, + loi_pad=1, + ): + self.filename = filename + self.color = color + self.verbose = verbose + self.line_number = line_number + self.last_line = last_line + self.margin = margin + self.mark_lois = mark_lois + self.header_max = header_max + self.loi_pad = loi_pad + self.show_top_of_file_parent_scope = show_top_of_file_parent_scope + + self.parent_context = parent_context + self.child_context = child_context + + lang = filename_to_lang(filename) + if not lang: + raise ValueError(f"Unknown language for {filename}") + + # Get parser based on file extension + parser = get_parser(lang) + tree = parser.parse(bytes(code, "utf8")) + + self.lines = code.splitlines() + self.num_lines = len(self.lines) + 1 + + # color lines, with highlighted matches + self.output_lines = dict() + + # Which scopes is each line part of? + # A scope is the line number on which the scope started + self.scopes = [set() for _ in range(self.num_lines)] + + # Which lines serve as a short "header" for the scope starting on that line + self.header = [list() for _ in range(self.num_lines)] + + self.nodes = [list() for _ in range(self.num_lines)] + + root_node = tree.root_node + self.walk_tree(root_node) + + if self.verbose: + scope_width = max(len(str(set(self.scopes[i]))) for i in range(self.num_lines - 1)) + for i in range(self.num_lines): + header = sorted(self.header[i]) + if self.verbose and i < self.num_lines - 1: + scopes = str(sorted(set(self.scopes[i]))) + print(f"{scopes.ljust(scope_width)}", i, self.lines[i]) + + if len(header) > 1: + size, head_start, head_end = header[0] + if size > self.header_max: + head_end = head_start + self.header_max + else: + head_start = i + head_end = i + 1 + + self.header[i] = head_start, head_end + + self.show_lines = set() + self.lines_of_interest = set() + + return + + def grep(self, pat, ignore_case): + found = set() + for i, line in enumerate(self.lines): + if re.search(pat, line, re.IGNORECASE if ignore_case else 0): + if self.color: + highlighted_line = re.sub( + pat, + lambda match: f"\033[1;31m{match.group()}\033[0m", # noqa + line, + flags=re.IGNORECASE if ignore_case else 0, + ) + self.output_lines[i] = highlighted_line + found.add(i) + return found + + def add_lines_of_interest(self, line_nums): + self.lines_of_interest.update(line_nums) + + def add_context(self): + if not self.lines_of_interest: + return + + self.done_parent_scopes = set() + + self.show_lines = set(self.lines_of_interest) + + if self.loi_pad: + for line in list(self.show_lines): + for new_line in range(line - self.loi_pad, line + self.loi_pad + 1): + # if not self.scopes[line].intersection(self.scopes[new_line]): + # continue + if new_line >= self.num_lines: + continue + if new_line < 0: + continue + self.show_lines.add(new_line) + + if self.last_line: + # add the bottom line (plus parent context) + bottom_line = self.num_lines - 2 + self.show_lines.add(bottom_line) + self.add_parent_scopes(bottom_line) + + if self.parent_context: + for i in set(self.lines_of_interest): + self.add_parent_scopes(i) + + if self.child_context: + for i in set(self.lines_of_interest): + self.add_child_context(i) + + # add the top margin lines of the file + if self.margin: + self.show_lines.update(range(self.margin)) + + self.close_small_gaps() + + def add_child_context(self, i): + if not self.nodes[i]: + return + + last_line = self.get_last_line_of_scope(i) + size = last_line - i + if size < 5: + self.show_lines.update(range(i, last_line + 1)) + return + + children = [] + for node in self.nodes[i]: + children += self.find_all_children(node) + + children = sorted( + children, + key=lambda node: node.end_point[0] - node.start_point[0], + reverse=True, + ) + + currently_showing = len(self.show_lines) + max_to_show = 25 + min_to_show = 5 + percent_to_show = 0.10 + max_to_show = max(min(size * percent_to_show, max_to_show), min_to_show) + + for child in children: + if len(self.show_lines) > currently_showing + max_to_show: + break + child_start_line = child.start_point[0] + self.add_parent_scopes(child_start_line) + + def find_all_children(self, node): + children = [node] + for child in node.children: + children += self.find_all_children(child) + return children + + def get_last_line_of_scope(self, i): + last_line = max(node.end_point[0] for node in self.nodes[i]) + return last_line + + def close_small_gaps(self): + # a "closing" operation on the integers in set. + # if i and i+2 are in there but i+1 is not, I want to add i+1 + # Create a new set for the "closed" lines + closed_show = set(self.show_lines) + sorted_show = sorted(self.show_lines) + for i in range(len(sorted_show) - 1): + if sorted_show[i + 1] - sorted_show[i] == 2: + closed_show.add(sorted_show[i] + 1) + + # pick up adjacent blank lines + for i, line in enumerate(self.lines): + if i not in closed_show: + continue + if self.lines[i].strip() and i < self.num_lines - 2 and not self.lines[i + 1].strip(): + closed_show.add(i + 1) + + self.show_lines = closed_show + + def format(self): + if not self.show_lines: + return "" + + output = "" + if self.color: + # reset + output += "\033[0m\n" + + dots = not (0 in self.show_lines) + for i, line in enumerate(self.lines): + if i not in self.show_lines: + if dots: + if self.line_number: + output += "...⋮...\n" + else: + output += "⋮\n" + dots = False + continue + + if i in self.lines_of_interest and self.mark_lois: + spacer = "█" + if self.color: + spacer = f"\033[31m{spacer}\033[0m" + else: + spacer = "│" + + line_output = f"{spacer}{self.output_lines.get(i, line)}" + if self.line_number: + line_output = f"{i + 1: 3}" + line_output + output += line_output + "\n" + + dots = True + + return output + + def add_parent_scopes(self, i): + if i in self.done_parent_scopes: + return + self.done_parent_scopes.add(i) + + if i >= len(self.scopes): + return + + for line_num in self.scopes[i]: + head_start, head_end = self.header[line_num] + if head_start > 0 or self.show_top_of_file_parent_scope: + self.show_lines.update(range(head_start, head_end)) + + if self.last_line: + last_line = self.get_last_line_of_scope(line_num) + self.add_parent_scopes(last_line) + + def walk_tree(self, node, depth=0): + start = node.start_point + end = node.end_point + + start_line = start[0] + end_line = end[0] + size = end_line - start_line + + self.nodes[start_line].append(node) + + # dump(start_line, end_line, node.text) + if self.verbose and node.is_named: + """ + for k in dir(node): + print(k, getattr(node, k)) + """ + print( + " " * depth, + node.type, + f"{start_line}-{end_line}={size + 1}", + node.text.splitlines()[0], + self.lines[start_line], + ) + + if size: + self.header[start_line].append((size, start_line, end_line)) + + for i in range(start_line, end_line + 1): + self.scopes[i].add(start_line) + + for child in node.children: + self.walk_tree(child, depth + 1) + + return start_line, end_line diff --git a/cecli/helpers/grep_ast/main.py b/cecli/helpers/grep_ast/main.py new file mode 100644 index 00000000000..bcf187304d5 --- /dev/null +++ b/cecli/helpers/grep_ast/main.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python + +import argparse +import os +import sys +from pathlib import Path + +import pathspec + +from .dump import dump # noqa: F401 +from .grep_ast import TreeContext +from .parsers import PARSERS + + +def main(): + # Parse command line arguments + parser = argparse.ArgumentParser() + parser.add_argument("pattern", nargs="?", help="the pattern to search for") + parser.add_argument("filenames", nargs="*", help="the files to display", default=".") + parser.add_argument("--encoding", default="utf8", help="file encoding") + parser.add_argument("--languages", action="store_true", help="show supported languages") + parser.add_argument("-i", "--ignore-case", action="store_true", help="ignore case distinctions") + parser.add_argument("--color", action="store_true", help="force color printing", default=None) + parser.add_argument( + "--no-color", action="store_false", help="disable color printing", dest="color" + ) + parser.add_argument("--no-gitignore", action="store_true", help="ignore .gitignore file") + parser.add_argument("--verbose", action="store_true", help="enable verbose output") + parser.add_argument("-n", "--line-number", action="store_true", help="display line numbers") + args = parser.parse_args() + + # If stdout is not a terminal, set color to False + if args.color is None: + args.color = os.isatty(1) + + # If --languages is provided, print the parsers table and exit + if args.languages: + for ext, lang in sorted(PARSERS.items()): + print(f"{ext}: {lang}") + return + elif not args.pattern: + print("Please provide a pattern to search for") + return 1 + + gitignore = None + if not args.no_gitignore: + for parent in Path("./xxx").resolve().parents: + potential_gitignore = parent / ".gitignore" + if potential_gitignore.exists(): + gitignore = potential_gitignore + break + + if gitignore: + with gitignore.open() as f: + spec = pathspec.PathSpec.from_lines("gitwildmatch", f) + else: + spec = pathspec.PathSpec.from_lines("gitwildmatch", []) + + for fname in enumerate_files(args.filenames, spec): + process_filename(fname, args) + + +def enumerate_files(fnames, spec, use_spec=False): + for fname in fnames: + fname = Path(fname) + + # oddly, Path('.').name == "" so we will recurse it + if fname.name.startswith(".") or use_spec and spec.match_file(fname): + continue + + if fname.is_file(): + yield str(fname) + continue + + if fname.is_dir(): + for sub_fnames in enumerate_files(fname.iterdir(), spec, True): + yield sub_fnames + + +def process_filename(filename, args): + try: + with open(filename, "r", encoding=args.encoding) as file: + code = file.read() + except UnicodeDecodeError: + return + + try: + tc = TreeContext( + filename, code, color=args.color, verbose=args.verbose, line_number=args.line_number + ) + except ValueError: + return + + loi = tc.grep(args.pattern, args.ignore_case) + if not loi: + return + + tc.add_lines_of_interest(loi) + tc.add_context() + + print() + print(f"{filename}:") + + print(tc.format(), end="") + + print() + + +if __name__ == "__main__": + res = main() + sys.exit(res) diff --git a/cecli/helpers/grep_ast/parsers.py b/cecli/helpers/grep_ast/parsers.py new file mode 100644 index 00000000000..13ffaa00bbb --- /dev/null +++ b/cecli/helpers/grep_ast/parsers.py @@ -0,0 +1,310 @@ +import os + +from .tsl import USING_TSL_PACK + +# Updated mapping of file extensions to parsers +PARSERS = { + ".py": "python", + ".js": "javascript", + ".mjs": "javascript", # mjs file extension stands for "module JavaScript." + ".go": "go", + ".bash": "bash", + ".c": "c", + ".cc": "cpp", + ".cs": "c_sharp", + ".cl": "commonlisp", + ".cpp": "cpp", + ".css": "css", + ".dockerfile": "dockerfile", + ".dot": "dot", + ".el": "elisp", + ".ex": "elixir", + ".elm": "elm", + ".et": "embedded_template", + ".erl": "erlang", + ".gomod": "gomod", + ".hack": "hack", + ".hs": "haskell", + ".hcl": "hcl", + ".html": "html", + ".java": "java", + ".jsdoc": "jsdoc", + ".json": "json", + ".jl": "julia", + ".kt": "kotlin", + ".lua": "lua", + ".mk": "make", + ".md": "markdown", # https://github.com/ikatyang/tree-sitter-markdown/issues/59 + ".m": "objc", + ".ml": "ocaml", + ".mli": "ocaml_interface", + ".pl": "perl", + ".php": "php", + ".ql": "ql", + ".r": "r", + ".R": "r", + ".regex": "regex", + ".rst": "rst", + ".rb": "ruby", + ".rs": "rust", + ".scala": "scala", + ".sql": "sql", + ".sqlite": "sqlite", + ".tf": "hcl", + ".toml": "toml", + ".tsq": "tsq", + ".tsx": "typescript", + ".ts": "typescript", + ".yaml": "yaml", +} + +if USING_TSL_PACK: + # Replace the PARSERS dictionary with a comprehensive mapping based on the language pack + PARSERS = { + # A + ".as": "actionscript", + ".adb": "ada", + ".ads": "ada", + ".agda": "agda", + ".ino": "arduino", + ".asm": "asm", + ".s": "asm", + ".astro": "astro", + # B + ".sh": "bash", + ".bash": "bash", + ".zsh": "bash", + ".bean": "beancount", + ".bib": "bibtex", + ".bicep": "bicep", + ".bb": "bitbake", + ".bbappend": "bitbake", + ".bbclass": "bitbake", + # C + ".c": "c", + ".h": "c", + ".cairo": "cairo", + ".capnp": "capnp", + ".chatito": "chatito", + ".clar": "clarity", + ".clj": "clojure", + ".cljs": "clojure", + ".cljc": "clojure", + ".end": "clojure", + ".cmake": "cmake", + "CMakeLists.txt": "cmake", + ".lisp": "commonlisp", + ".cl": "commonlisp", + ".cpon": "cpon", + ".cpp": "cpp", + ".cc": "cpp", + ".cxx": "cpp", + ".hpp": "cpp", + ".hxx": "cpp", + ".h++": "cpp", + ".cs": "csharp", + ".css": "css", + ".csv": "csv", + ".cu": "cuda", + ".cuh": "cuda", + ".d": "d", + # D + ".dart": "dart", + "Dockerfile": "dockerfile", + ".dtd": "dtd", + # E + ".el": "elisp", + ".ex": "elixir", + ".exs": "elixir", + ".elm": "elm", + ".erl": "erlang", + ".hrl": "erlang", + # F + ".fnl": "fennel", + ".fir": "firrtl", + ".fish": "fish", + ".f": "fortran", + ".f90": "fortran", + ".f95": "fortran", + ".f03": "fortran", + ".f08": "fortran", + ".fc": "func", + # G + ".gd": "gdscript", + ".gitattributes": "gitattributes", + ".gitcommit": "gitcommit", + ".gitignore": "gitignore", + ".gleam": "gleam", + ".glsl": "glsl", + ".vert": "glsl", + ".frag": "glsl", + ".gn": "gn", + ".gni": "gn", + ".go": "go", + "go.mod": "gomod", + "go.sum": "gosum", + ".groovy": "groovy", + ".launch": "gstlaunch", + # H + ".hack": "hack", + ".ha": "hare", + ".hs": "haskell", + ".hx": "haxe", + ".hcl": "hcl", + ".tf": "hcl", + ".tfvars": "hcl", + ".heex": "heex", + ".hlsl": "hlsl", + ".html": "html", + ".htm": "html", + ".hypr": "hyprlang", + # I + ".ispc": "ispc", + # J + ".janet": "janet", + ".java": "java", + ".js": "javascript", + ".jsx": "javascript", + ".mjs": "javascript", + ".jsdoc": "jsdoc", + ".json": "json", + ".jsonnet": "jsonnet", + ".libsonnet": "jsonnet", + ".jl": "julia", + # K + "Kconfig": "kconfig", + ".kdl": "kdl", + ".kt": "kotlin", + ".kts": "kotlin", + # L + ".tex": "latex", + ".sty": "latex", + ".cls": "latex", + ".ld": "linkerscript", + ".ll": "llvm", + ".td": "tablegen", + ".lua": "lua", + ".luadoc": "luadoc", + ".luap": "luap", + ".luau": "luau", + # M + ".magik": "magik", + "Makefile": "make", + ".mk": "make", + ".md": "markdown", + ".markdown": "markdown", + ".m": "matlab", # Note: .m is used by both MATLAB and Objective-C, prioritizing MATLAB here + ".mat": "matlab", + ".mermaid": "mermaid", + "meson.build": "meson", + # N + ".ninja": "ninja", + ".nix": "nix", + ".nqc": "nqc", + # O + # .m extension is handled under MATLAB section (dual use extension) + ".mm": "objc", + ".ml": "ocaml", + ".mli": "ocaml_interface", + ".odin": "odin", + ".org": "org", + # P + ".pas": "pascal", + ".pp": "pascal", + ".pem": "pem", + ".pl": "perl", + ".pm": "perl", + ".pgn": "pgn", + ".php": "php", + ".po": "po", + ".pot": "po", + ".pony": "pony", + ".ps1": "powershell", + ".psm1": "powershell", + ".printf": "printf", + ".prisma": "prisma", + ".properties": "properties", + ".proto": "proto", + ".psv": "psv", + ".purs": "purescript", + "MANIFEST.in": "pymanifest", + ".py": "python", + # Q + "qmldir": "qmldir", + ".qml": "qmljs", + # Q + # R + ".r": "r", + ".R": "r", + ".rkt": "racket", + ".re2c": "re2c", + ".inputrc": "readline", + "requirements.txt": "requirements", + ".ron": "ron", + ".rst": "rst", + ".rb": "ruby", + ".rs": "rust", + # S + ".scala": "scala", + ".sc": "scala", + ".scm": "scheme", # .scm is primarily used for Scheme files + ".ss": "scheme", + ".scss": "scss", + ".smali": "smali", + ".smithy": "smithy", + ".sol": "solidity", + ".rq": "sparql", + ".sql": "sql", + ".nut": "squirrel", + ".bzl": "starlark", + "BUILD": "starlark", + "WORKSPACE": "starlark", + ".svelte": "svelte", + ".swift": "swift", + # T + ".tcl": "tcl", + ".thrift": "thrift", + ".toml": "toml", + ".tsv": "tsv", + ".tsx": "typescript", + ".twig": "twig", + ".ts": "typescript", + ".typ": "typst", + # U + ".rules": "udev", + ".ungram": "ungrammar", + ".tal": "uxntal", + # V + # Note: .v extension is used by both V language and Verilog + # Prioritizing Verilog as it's more commonly used + ".sv": "verilog", + ".v": "verilog", + # For V language, users may need to specify parser manually + ".vhd": "vhdl", + ".vhdl": "vhdl", + ".vim": "vim", + ".vimrc": "vim", + ".vue": "vue", + # W + ".wgsl": "wgsl", + # X + ".XCompose": "xcompose", + ".xml": "xml", + ".svg": "xml", + ".xsl": "xml", + # Y + ".yuck": "yuck", + # Z + ".zig": "zig", + } + + +def filename_to_lang(filename): + # First check if the full filename (like "Dockerfile" or "go.mod") is in PARSERS + basename = os.path.basename(filename) + if basename in PARSERS: + return PARSERS[basename] + + # If not found by full filename, check by extension + file_extension = os.path.splitext(filename)[1] + return PARSERS.get(file_extension) diff --git a/cecli/helpers/grep_ast/tsl.py b/cecli/helpers/grep_ast/tsl.py new file mode 100644 index 00000000000..47f2d7805cb --- /dev/null +++ b/cecli/helpers/grep_ast/tsl.py @@ -0,0 +1,10 @@ +try: + from tree_sitter_language_pack import get_language, get_parser + + USING_TSL_PACK = True +except ImportError: + from tree_sitter_languages import get_language, get_parser + + USING_TSL_PACK = False + +__all__ = [get_parser, get_language, USING_TSL_PACK] diff --git a/cecli/linter.py b/cecli/linter.py index 065cdaad638..6b04ab546ff 100644 --- a/cecli/linter.py +++ b/cecli/linter.py @@ -8,10 +8,10 @@ from pathlib import Path import oslex -from grep_ast import TreeContext, filename_to_lang -from grep_ast.tsl import get_parser # noqa: E402 from cecli.dump import dump # noqa: F401 +from cecli.helpers.grep_ast import TreeContext, filename_to_lang +from cecli.helpers.grep_ast.tsl import get_parser # noqa: E402 from cecli.run_cmd import run_cmd_subprocess # noqa: F401 # tree_sitter is throwing a FutureWarning diff --git a/cecli/models.py b/cecli/models.py index 022b1723a9d..8cf915860eb 100644 --- a/cecli/models.py +++ b/cecli/models.py @@ -1008,7 +1008,10 @@ async def send_completion( if effective_tools is None and functions: effective_tools = [dict(type="function", function=f) for f in functions] if effective_tools: - kwargs["tools"] = effective_tools + sorted_tools = sorted( + effective_tools, key=lambda x: x.get("function", {}).get("name", "Invalid Name") + ) + kwargs["tools"] = sorted_tools if functions and len(functions) == 1: function = functions[0] if "name" in function: @@ -1051,6 +1054,11 @@ async def send_completion( "Copilot-Integration-Id": "vscode-chat", } + if kwargs.get("headers", None): + kwargs["headers"].update({"Connection": "close"}) + else: + kwargs["headers"] = {"Connection": "close"} + litellm_ex = LiteLLMExceptions() retry_delay = 0.125 diff --git a/cecli/onboarding.py b/cecli/onboarding.py index 63470bb88c8..dbe0ae48e0c 100644 --- a/cecli/onboarding.py +++ b/cecli/onboarding.py @@ -165,7 +165,7 @@ def start_openrouter_oauth_flow(io): class OAuthCallbackHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): - nonlocal auth_code, server_error + nonlocal auth_code, server_error # noqa parsed_path = urlparse(self.path) if parsed_path.path == "/callback/cecli": query_params = parse_qs(parsed_path.query) @@ -193,7 +193,7 @@ def log_message(self, format, *args): pass def run_server(): - nonlocal server_error + nonlocal server_error # noqa try: with socketserver.TCPServer(("localhost", port), OAuthCallbackHandler) as httpd: io.tool_output(f"Temporary server listening on {callback_url}", log_only=True) @@ -203,7 +203,7 @@ def run_server(): time.sleep(0.1) io.tool_output("Shutting down temporary server.", log_only=True) except Exception as e: - server_error = f"Failed to start or run temporary server: {e}" + server_error = f"Failed to start or run temporary server: {e}" # noqa server_started.set() shutdown_server.set() diff --git a/cecli/repomap.py b/cecli/repomap.py index bb95e92b537..99812879ad7 100644 --- a/cecli/repomap.py +++ b/cecli/repomap.py @@ -12,7 +12,6 @@ import tree_sitter from diskcache import Cache -from grep_ast import TreeContext, filename_to_lang from pygments.lexers import guess_lexer_for_filename from pygments.token import Token @@ -27,7 +26,13 @@ # tree_sitter is throwing a FutureWarning warnings.simplefilter("ignore", category=FutureWarning) -from grep_ast.tsl import USING_TSL_PACK, get_language, get_parser # noqa: E402 + +from cecli.helpers.grep_ast import TreeContext, filename_to_lang # noqa: E402 +from cecli.helpers.grep_ast.tsl import ( # noqa: E402 + USING_TSL_PACK, + get_language, + get_parser, +) # Define the Tag namedtuple with a default for specific_kind to maintain compatibility @@ -1435,7 +1440,7 @@ def get_scm_fname(lang): def get_supported_languages_md(): - from grep_ast.parsers import PARSERS + from cecli.helpers.grep_ast.parsers import PARSERS res = """ | Language | File extension | Repo map | Linter | diff --git a/cecli/report.py b/cecli/report.py index 45a7ba9537b..44127029eb9 100644 --- a/cecli/report.py +++ b/cecli/report.py @@ -78,22 +78,22 @@ def format_args_for_reporting(args): def get_args_error_data(): - global resolved_args_data + global resolved_args_data # noqa return resolved_args_data def set_args_error_data(args): - global resolved_args_data + global resolved_args_data # noqa resolved_args_data = args def get_error_prefix(): - global error_prefix + global error_prefix # noqa return error_prefix def update_error_prefix(prefix): - global error_prefix + global error_prefix # noqa error_prefix.append(f"{prefix}\n") error_prefix = error_prefix[-10:] diff --git a/cecli/resources/model-metadata.json b/cecli/resources/model-metadata.json index 4e6afc79097..29103348f7d 100644 --- a/cecli/resources/model-metadata.json +++ b/cecli/resources/model-metadata.json @@ -410,6 +410,20 @@ "output_cost_per_token": 0.0000024, "supports_tool_choice": true }, + "anthropic.claude-mythos-preview": { + "input_cost_per_token": 0, + "output_cost_per_token": 0, + "litellm_provider": "bedrock", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_tool_choice": true + }, "anthropic.claude-opus-4-1-20250805-v1:0": { "cache_creation_input_token_cost": 0.00001875, "cache_read_input_token_cost": 0.0000015, @@ -514,7 +528,39 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "anthropic.claude-opus-4-7": { + "cache_creation_input_token_cost": 0.00000625, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 0.00000375, @@ -602,7 +648,8 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_minimal_reasoning_effort": true }, "anthropic.claude-v1": { "input_cost_per_token": 0.000008, @@ -984,7 +1031,39 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "au.anthropic.claude-opus-4-7": { + "cache_creation_input_token_cost": 0.000006875, + "cache_read_input_token_cost": 5.5e-7, + "input_cost_per_token": 0.0000055, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.0000275, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "au.anthropic.claude-sonnet-4-5-20250929-v1:0": { "cache_creation_input_token_cost": 0.000004125, @@ -1042,7 +1121,8 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_minimal_reasoning_effort": true }, "azure/command-r-plus": { "input_cost_per_token": 0.000003, @@ -4316,7 +4396,39 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "tool_use_system_prompt_tokens": 159 + "tool_use_system_prompt_tokens": 159, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "azure_ai/claude-opus-4-7": { + "input_cost_per_token": 0.000005, + "output_cost_per_token": 0.000025, + "litellm_provider": "azure_ai", + "max_input_tokens": 200000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "cache_creation_input_token_cost": 0.00000625, + "cache_creation_input_token_cost_above_1hr": 0.00001, + "cache_read_input_token_cost": 5e-7, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 159, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "azure_ai/claude-sonnet-4-5": { "cache_creation_input_token_cost": 0.00000375, @@ -4359,7 +4471,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "tool_use_system_prompt_tokens": 346 + "tool_use_system_prompt_tokens": 346, + "supports_minimal_reasoning_effort": true }, "azure_ai/deepseek-r1": { "input_cost_per_token": 0.00000135, @@ -7124,7 +7237,9 @@ "provider_specific_entry": { "us": 1.1, "fast": 6 - } + }, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "claude-opus-4-6-20260205": { "cache_creation_input_token_cost": 0.00000625, @@ -7155,7 +7270,77 @@ "provider_specific_entry": { "us": 1.1, "fast": 6 - } + }, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "claude-opus-4-7": { + "cache_creation_input_token_cost": 0.00000625, + "cache_creation_input_token_cost_above_1hr": 0.00001, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "anthropic", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "provider_specific_entry": { + "us": 1.1, + "fast": 6 + }, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "claude-opus-4-7-20260416": { + "cache_creation_input_token_cost": 0.00000625, + "cache_creation_input_token_cost_above_1hr": 0.00001, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "anthropic", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "provider_specific_entry": { + "us": 1.1, + "fast": 6 + }, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "claude-sonnet-4-20250514": { "deprecation_date": "2026-05-14", @@ -7299,7 +7484,8 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "tool_use_system_prompt_tokens": 346 + "tool_use_system_prompt_tokens": 346, + "supports_minimal_reasoning_effort": true }, "cloudflare/@cf/meta/llama-2-7b-chat-fp16": { "input_cost_per_token": 0.000001923, @@ -9978,7 +10164,39 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "eu.anthropic.claude-opus-4-7": { + "cache_creation_input_token_cost": 0.000006875, + "cache_read_input_token_cost": 5.5e-7, + "input_cost_per_token": 0.0000055, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.0000275, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "eu.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 0.00000375, @@ -10066,7 +10284,8 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_minimal_reasoning_effort": true }, "eu.deepseek.v3.2": { "input_cost_per_token": 7.4e-7, @@ -15746,7 +15965,39 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "global.anthropic.claude-opus-4-7": { + "cache_creation_input_token_cost": 0.00000625, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "global.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 0.00000375, @@ -15834,7 +16085,8 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_minimal_reasoning_effort": true }, "gmi/MiniMaxAI/MiniMax-M2.1": { "input_cost_per_token": 3e-7, @@ -17657,8 +17909,44 @@ "supports_xhigh_reasoning_effort": true, "supports_minimal_reasoning_effort": false }, - "gpt-audio": { - "input_cost_per_audio_token": 0.000032, + "gpt-5.5": { + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "openai", + "max_input_tokens": 272000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.00003, + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/batch", + "/v1/responses" + ], + "supported_modalities": [ + "text", + "image" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_native_streaming": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_service_tier": true, + "supports_vision": true, + "supports_none_reasoning_effort": true, + "supports_xhigh_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "gpt-audio": { + "input_cost_per_audio_token": 0.000032, "input_cost_per_token": 0.0000025, "litellm_provider": "openai", "max_input_tokens": 128000, @@ -20538,6 +20826,22 @@ "supports_video_input": true, "supports_vision": true }, + "moonshot/kimi-k2.6": { + "cache_read_input_token_cost": 1.6e-7, + "input_cost_per_token": 9.5e-7, + "litellm_provider": "moonshot", + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "mode": "chat", + "output_cost_per_token": 0.000004, + "source": "https://platform.kimi.ai/docs/pricing/chat-k26", + "supports_function_calling": true, + "supports_reasoning": true, + "supports_tool_choice": true, + "supports_video_input": true, + "supports_vision": true + }, "moonshot/kimi-latest": { "cache_read_input_token_cost": 1.5e-7, "input_cost_per_token": 0.000002, @@ -23403,6 +23707,29 @@ "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true, + "tool_use_system_prompt_tokens": 346, + "supports_minimal_reasoning_effort": true + }, + "openrouter/anthropic/claude-opus-4.7": { + "cache_creation_input_token_cost": 0.00000625, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "openrouter", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, "tool_use_system_prompt_tokens": 346 }, "openrouter/anthropic/claude-sonnet-4": { @@ -23475,7 +23802,8 @@ "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true, - "tool_use_system_prompt_tokens": 159 + "tool_use_system_prompt_tokens": 159, + "supports_minimal_reasoning_effort": true }, "openrouter/bytedance/ui-tars-1.5-7b": { "input_cost_per_token": 1e-7, @@ -23746,6 +24074,58 @@ "supports_vision": true, "supports_web_search": true }, + "openrouter/google/gemini-3.1-flash-lite-preview": { + "cache_read_input_token_cost": 2.5e-8, + "cache_read_input_token_cost_per_audio_token": 5e-8, + "input_cost_per_audio_token": 5e-7, + "input_cost_per_token": 2.5e-7, + "litellm_provider": "openrouter", + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_images_per_prompt": 3000, + "max_input_tokens": 1048576, + "max_output_tokens": 65536, + "max_pdf_size_mb": 30, + "max_tokens": 65536, + "max_video_length": 1, + "max_videos_per_prompt": 10, + "mode": "chat", + "output_cost_per_reasoning_token": 0.0000015, + "output_cost_per_token": 0.0000015, + "rpm": 2000, + "source": "https://ai.google.dev/pricing/gemini-3", + "supported_endpoints": [ + "/v1/chat/completions", + "/v1/completions", + "/v1/batch" + ], + "supported_modalities": [ + "text", + "image", + "audio", + "video" + ], + "supported_output_modalities": [ + "text" + ], + "supports_audio_input": true, + "supports_audio_output": false, + "supports_code_execution": true, + "supports_file_search": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_url_context": true, + "supports_video_input": true, + "supports_vision": true, + "supports_web_search": true, + "tpm": 800000 + }, "openrouter/google/gemini-3.1-pro-preview": { "cache_read_input_token_cost": 2e-7, "cache_read_input_token_cost_above_200k_tokens": 4e-7, @@ -26884,7 +27264,39 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "us.anthropic.claude-opus-4-7": { + "cache_creation_input_token_cost": 0.000006875, + "cache_read_input_token_cost": 5.5e-7, + "input_cost_per_token": 0.0000055, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.0000275, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_native_structured_output": true, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "us.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 0.00000375, @@ -26972,7 +27384,8 @@ "supports_tool_choice": true, "supports_vision": true, "tool_use_system_prompt_tokens": 346, - "supports_native_structured_output": true + "supports_native_structured_output": true, + "supports_minimal_reasoning_effort": true }, "us.deepseek.r1-v1:0": { "input_cost_per_token": 0.00000135, @@ -27556,7 +27969,8 @@ "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, - "supports_vision": true + "supports_vision": true, + "supports_minimal_reasoning_effort": true }, "vercel_ai_gateway/anthropic/claude-sonnet-4": { "cache_creation_input_token_cost": 0.00000375, @@ -28719,7 +29133,9 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "tool_use_system_prompt_tokens": 346 + "tool_use_system_prompt_tokens": 346, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "vertex_ai/claude-opus-4-6@default": { "cache_creation_input_token_cost": 0.00000625, @@ -28745,7 +29161,67 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, - "tool_use_system_prompt_tokens": 346 + "tool_use_system_prompt_tokens": 346, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "vertex_ai/claude-opus-4-7": { + "cache_creation_input_token_cost": 0.00000625, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "vertex_ai-anthropic_models", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true + }, + "vertex_ai/claude-opus-4-7@default": { + "cache_creation_input_token_cost": 0.00000625, + "cache_read_input_token_cost": 5e-7, + "input_cost_per_token": 0.000005, + "litellm_provider": "vertex_ai-anthropic_models", + "max_input_tokens": 1000000, + "max_output_tokens": 128000, + "max_tokens": 128000, + "mode": "chat", + "output_cost_per_token": 0.000025, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": false, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_xhigh_reasoning_effort": true, + "tool_use_system_prompt_tokens": 346, + "supports_max_reasoning_effort": true, + "supports_minimal_reasoning_effort": true }, "vertex_ai/claude-opus-4@20250514": { "cache_creation_input_token_cost": 0.00001875, @@ -28880,7 +29356,8 @@ "search_context_size_high": 0.01, "search_context_size_low": 0.01, "search_context_size_medium": 0.01 - } + }, + "supports_minimal_reasoning_effort": true }, "vertex_ai/claude-sonnet-4-6@default": { "cache_creation_input_token_cost": 0.00000375, @@ -28906,7 +29383,8 @@ "search_context_size_high": 0.01, "search_context_size_low": 0.01, "search_context_size_medium": 0.01 - } + }, + "supports_minimal_reasoning_effort": true }, "vertex_ai/claude-sonnet-4@20250514": { "cache_creation_input_token_cost": 0.00000375, @@ -30654,6 +31132,7 @@ "output_cost_per_token": 0.000015, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": false, "supports_tool_choice": true, "supports_web_search": true @@ -30669,6 +31148,7 @@ "output_cost_per_token": 0.000015, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": false, "supports_tool_choice": true, "supports_web_search": true @@ -30684,6 +31164,7 @@ "output_cost_per_token": 0.000025, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": false, "supports_tool_choice": true, "supports_web_search": true @@ -30699,6 +31180,7 @@ "output_cost_per_token": 0.000025, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": false, "supports_tool_choice": true, "supports_web_search": true @@ -30714,6 +31196,7 @@ "output_cost_per_token": 0.000015, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": false, "supports_tool_choice": true, "supports_web_search": true @@ -30730,6 +31213,7 @@ "output_cost_per_token": 5e-7, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": false, "supports_tool_choice": true, @@ -30747,6 +31231,7 @@ "output_cost_per_token": 5e-7, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": false, "supports_tool_choice": true, @@ -30763,6 +31248,7 @@ "output_cost_per_token": 0.000004, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": false, "supports_tool_choice": true, @@ -30779,6 +31265,7 @@ "output_cost_per_token": 0.000004, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": false, "supports_tool_choice": true, @@ -30795,6 +31282,7 @@ "output_cost_per_token": 0.000004, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": false, "supports_tool_choice": true, @@ -30811,6 +31299,7 @@ "output_cost_per_token": 5e-7, "source": "https://x.ai/api#pricing", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": false, "supports_tool_choice": true, @@ -30826,6 +31315,7 @@ "output_cost_per_token": 0.000015, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_tool_choice": true, "supports_web_search": true }, @@ -30841,6 +31331,7 @@ "output_cost_per_token_above_128k_tokens": 0.00003, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_tool_choice": true, "supports_web_search": true }, @@ -30858,6 +31349,7 @@ "source": "https://docs.x.ai/docs/models/grok-4-1-fast-reasoning", "supports_audio_input": true, "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, @@ -30878,6 +31370,7 @@ "source": "https://docs.x.ai/docs/models/grok-4-1-fast-non-reasoning", "supports_audio_input": true, "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, @@ -30897,6 +31390,7 @@ "source": "https://docs.x.ai/docs/models/grok-4-1-fast-non-reasoning", "supports_audio_input": true, "supports_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, @@ -30916,6 +31410,7 @@ "source": "https://docs.x.ai/docs/models/grok-4-1-fast-reasoning", "supports_audio_input": true, "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, @@ -30936,6 +31431,7 @@ "source": "https://docs.x.ai/docs/models/grok-4-1-fast-reasoning", "supports_audio_input": true, "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, @@ -30943,34 +31439,36 @@ "supports_web_search": true }, "xai/grok-4-fast-non-reasoning": { + "cache_read_input_token_cost": 5e-8, + "input_cost_per_token": 2e-7, + "input_cost_per_token_above_128k_tokens": 4e-7, "litellm_provider": "xai", "max_input_tokens": 2000000, "max_output_tokens": 2000000, - "cache_read_input_token_cost": 5e-8, "max_tokens": 2000000, "mode": "chat", - "input_cost_per_token": 2e-7, - "input_cost_per_token_above_128k_tokens": 4e-7, "output_cost_per_token": 5e-7, "output_cost_per_token_above_128k_tokens": 0.000001, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_tool_choice": true, "supports_web_search": true }, "xai/grok-4-fast-reasoning": { + "cache_read_input_token_cost": 5e-8, + "input_cost_per_token": 2e-7, + "input_cost_per_token_above_128k_tokens": 4e-7, "litellm_provider": "xai", "max_input_tokens": 2000000, "max_output_tokens": 2000000, "max_tokens": 2000000, "mode": "chat", - "input_cost_per_token": 2e-7, - "input_cost_per_token_above_128k_tokens": 4e-7, "output_cost_per_token": 5e-7, "output_cost_per_token_above_128k_tokens": 0.000001, - "cache_read_input_token_cost": 5e-8, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_tool_choice": true, "supports_web_search": true }, @@ -30986,9 +31484,26 @@ "output_cost_per_token_above_128k_tokens": 0.00003, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_tool_choice": true, "supports_web_search": true }, + "xai/grok-4.20-0309-reasoning": { + "cache_read_input_token_cost": 2e-7, + "input_cost_per_token": 0.000002, + "litellm_provider": "xai", + "max_input_tokens": 2000000, + "max_output_tokens": 2000000, + "max_tokens": 2000000, + "mode": "chat", + "output_cost_per_token": 0.000006, + "source": "https://docs.x.ai/docs/models", + "supports_function_calling": true, + "supports_reasoning": true, + "supports_tool_choice": true, + "supports_vision": true, + "supports_web_search": true + }, "xai/grok-4.20-beta-0309-non-reasoning": { "cache_read_input_token_cost": 2e-7, "input_cost_per_token": 0.000002, @@ -31000,6 +31515,7 @@ "output_cost_per_token": 0.000006, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_tool_choice": true, "supports_vision": true, "supports_web_search": true @@ -31015,6 +31531,7 @@ "output_cost_per_token": 0.000006, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true, @@ -31031,6 +31548,7 @@ "output_cost_per_token": 0.000006, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_tool_choice": true, "supports_vision": true, @@ -31060,6 +31578,7 @@ "output_cost_per_token": 0.0000015, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_tool_choice": true }, @@ -31074,6 +31593,7 @@ "output_cost_per_token": 0.0000015, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_tool_choice": true }, @@ -31088,6 +31608,7 @@ "output_cost_per_token": 0.0000015, "source": "https://docs.x.ai/docs/models", "supports_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_tool_choice": true }, diff --git a/cecli/sessions.py b/cecli/sessions.py index b226def8ef5..5d8447d5213 100644 --- a/cecli/sessions.py +++ b/cecli/sessions.py @@ -71,9 +71,10 @@ def list_sessions(self) -> List[Dict]: "file": session_file, "model": session_data.get("model", "unknown"), "edit_format": session_data.get("edit_format", "unknown"), - "num_messages": len( - session_data.get("chat_history", {}).get("done_messages", []) - ) + len(session_data.get("chat_history", {}).get("cur_messages", [])), + "num_messages": ( + len(session_data.get("chat_history", {}).get("done_messages", [])) + + len(session_data.get("chat_history", {}).get("cur_messages", [])) + ), "num_files": ( len(session_data.get("files", {}).get("editable", [])) + len(session_data.get("files", {}).get("read_only", [])) @@ -149,11 +150,11 @@ def _build_session_data(self, session_name) -> Dict: "editor_edit_format": self.coder.main_model.editor_edit_format, "edit_format": self.coder.edit_format, "chat_history": { - "done_messages": ConversationService.get_manager(self.coder).get_messages_dict( - MessageTag.DONE + "done_messages": ( + ConversationService.get_manager(self.coder).get_messages_dict(MessageTag.DONE) ), - "cur_messages": ConversationService.get_manager(self.coder).get_messages_dict( - MessageTag.CUR + "cur_messages": ( + ConversationService.get_manager(self.coder).get_messages_dict(MessageTag.CUR) ), }, "files": { diff --git a/cecli/watch.py b/cecli/watch.py index a573e739eff..214de576db3 100644 --- a/cecli/watch.py +++ b/cecli/watch.py @@ -3,11 +3,11 @@ from pathlib import Path from typing import Optional -from grep_ast import TreeContext from pathspec import PathSpec from pathspec.patterns import GitWildMatchPattern from cecli.dump import dump # noqa +from cecli.helpers.grep_ast import TreeContext from cecli.watch_prompts import watch_ask_prompt, watch_code_prompt diff --git a/requirements.txt b/requirements.txt index 5b81c0115bd..514cc1c7bad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -109,10 +109,6 @@ gitpython==3.1.45 # via # -c requirements/common-constraints.txt # -r requirements/requirements.in -grep-ast==0.9.0 - # via - # -c requirements/common-constraints.txt - # -r requirements/requirements.in h11==0.16.0 # via # -c requirements/common-constraints.txt @@ -244,7 +240,6 @@ pathspec==0.12.1 # via # -c requirements/common-constraints.txt # -r requirements/requirements.in - # grep-ast pexpect==4.9.0 # via # -c requirements/common-constraints.txt @@ -434,7 +429,8 @@ tqdm==4.67.1 # -c requirements/common-constraints.txt # -r requirements/requirements.in # tree-sitter-language-pack -tree-sitter-c-sharp==0.23.1 + # tree-sitter-languages +tree-sitter-c-sharp==0.23.5 # via # -c requirements/common-constraints.txt # tree-sitter-language-pack @@ -442,10 +438,14 @@ tree-sitter-embedded-template==0.25.0 # via # -c requirements/common-constraints.txt # tree-sitter-language-pack -tree-sitter-language-pack==0.11.0 +tree-sitter-language-pack==0.13.0 # via # -c requirements/common-constraints.txt - # grep-ast + # -r requirements/requirements.in +tree-sitter-languages==1.10.2 + # via + # -c requirements/common-constraints.txt + # -r requirements/requirements.in tree-sitter-yaml==0.7.2 # via # -c requirements/common-constraints.txt diff --git a/requirements/common-constraints.txt b/requirements/common-constraints.txt index c1c84b6ab26..fd9e4dc92c8 100644 --- a/requirements/common-constraints.txt +++ b/requirements/common-constraints.txt @@ -117,8 +117,6 @@ greenlet==3.2.4 # via # playwright # sqlalchemy -grep-ast==0.9.0 - # via -r requirements/requirements.in griffe==1.15.0 # via banks h11==0.16.0 @@ -308,9 +306,7 @@ packaging==25.0 pandas==2.3.3 # via -r requirements/requirements-dev.in pathspec==0.12.1 - # via - # -r requirements/requirements.in - # grep-ast + # via -r requirements/requirements.in pexpect==4.9.0 # via -r requirements/requirements.in pillow==12.0.0 @@ -526,12 +522,15 @@ tree-sitter==0.25.2 # via # -r requirements/requirements.in # tree-sitter-language-pack -tree-sitter-c-sharp==0.23.1 + # tree-sitter-languages +tree-sitter-c-sharp==0.23.5 # via tree-sitter-language-pack tree-sitter-embedded-template==0.25.0 # via tree-sitter-language-pack -tree-sitter-language-pack==0.11.0 - # via grep-ast +tree-sitter-language-pack==0.13.0 + # via -r requirements/requirements.in +tree-sitter-languages==1.10.2 + # via -r requirements/requirements.in tree-sitter-yaml==0.7.2 # via tree-sitter-language-pack triton==3.5.1 diff --git a/requirements/requirements.in b/requirements/requirements.in index 1c13b060815..895678008c9 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -7,7 +7,6 @@ prompt_toolkit backoff>=2.2.1 pathspec>=0.12.1 diskcache>=5.6.3 -grep_ast packaging>=25.0 sounddevice>=0.5.2 soundfile>=0.13.1 @@ -47,4 +46,6 @@ importlib-metadata>=7.2.1 tomli>=2.3.0; python_version <= "3.10" tree-sitter==0.23.2; python_version < "3.10" -tree-sitter>=0.25.1; python_version >= "3.10" \ No newline at end of file +tree-sitter>=0.25.1; python_version >= "3.10" +tree-sitter-language-pack<=0.13.0 +tree_sitter_languages==1.10.2; python_version <= "3.12" \ No newline at end of file diff --git a/scripts/30k-image.py b/scripts/30k-image.py index 6b7938c6cd2..d2a0df71de1 100644 --- a/scripts/30k-image.py +++ b/scripts/30k-image.py @@ -135,14 +135,18 @@ def generate_celebration_svg(output_path=None, width=DEFAULT_WIDTH, height=DEFAU # Font embedding font_data = embed_font() - font_face = f""" + font_face = ( + f""" @font-face {{ font-family: 'GlassTTYVT220'; src: url(data:font/truetype;charset=utf-8;base64,{font_data}) format('truetype'); font-weight: normal; font-style: normal; }} - """ if font_data else "" + """ + if font_data + else "" + ) # Generate confetti elements confetti = generate_confetti(count=150, width=width, height=height) diff --git a/tests/basic/test_models.py b/tests/basic/test_models.py index 1114b6c11a8..5da3de76669 100644 --- a/tests/basic/test_models.py +++ b/tests/basic/test_models.py @@ -382,6 +382,7 @@ async def test_ollama_num_ctx_set_when_missing(self, mock_token_count, mock_comp num_ctx=expected_ctx, timeout=600, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) @@ -390,11 +391,14 @@ async def test_modern_tool_call_propagation(self, mock_completion): model = Model("gpt-4") messages = [{"role": "user", "content": "Hello"}] await model.send_completion( - messages, functions=None, stream=False, tools=[dict(type="function", function="test")] + messages, + functions=None, + stream=False, + tools=[dict(type="function", function=dict(name="test"))], ) mock_completion.assert_called_once() call_kwargs = mock_completion.call_args.kwargs - assert call_kwargs["tools"] == [dict(type="function", function="test")] + assert call_kwargs["tools"] == [dict(type="function", function=dict(name="test"))] assert call_kwargs["model"] == model.name assert call_kwargs["stream"] is False @@ -402,10 +406,10 @@ async def test_modern_tool_call_propagation(self, mock_completion): async def test_legacy_tool_call_propagation(self, mock_completion): model = Model("gpt-4") messages = [{"role": "user", "content": "Hello"}] - await model.send_completion(messages, functions=["test"], stream=False) + await model.send_completion(messages, functions=[dict(name="test")], stream=False) mock_completion.assert_called_once() call_kwargs = mock_completion.call_args.kwargs - assert call_kwargs["tools"] == [dict(type="function", function="test")] + assert call_kwargs["tools"] == [dict(type="function", function=dict(name="test"))] assert call_kwargs["model"] == model.name assert call_kwargs["stream"] is False @@ -423,6 +427,7 @@ async def test_ollama_uses_existing_num_ctx(self, mock_completion): num_ctx=4096, timeout=600, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) @@ -439,6 +444,7 @@ async def test_non_ollama_no_num_ctx(self, mock_completion): temperature=0, timeout=600, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) assert "num_ctx" not in mock_completion.call_args.kwargs @@ -471,6 +477,7 @@ async def test_request_timeout_default(self, mock_completion): temperature=0, timeout=600, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) @@ -488,6 +495,7 @@ async def test_request_timeout_from_extra_params(self, mock_completion): temperature=0, timeout=300, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) @@ -505,6 +513,7 @@ async def test_use_temperature_in_send_completion(self, mock_completion): temperature=0, timeout=600, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) @@ -527,6 +536,7 @@ async def test_use_temperature_in_send_completion(self, mock_completion): temperature=0.7, timeout=600, drop_params=True, + headers={"Connection": "close"}, cache_control_injection_points=ANY, ) diff --git a/tests/basic/test_sessions.py b/tests/basic/test_sessions.py index 7bc3a266849..adb5a01a907 100644 --- a/tests/basic/test_sessions.py +++ b/tests/basic/test_sessions.py @@ -106,8 +106,10 @@ async def test_cmd_load_session_basic(self): "read_only_stubs": [], }, "settings": {"auto_commits": True, "auto_lint": False, "auto_test": False}, - "todo_list": """Restored tasks -- item""", + "todo_list": ( + """Restored tasks +- item""" + ), } session_file = Path(handle_core_files(".cecli")) / "sessions" / "test_session.json" session_file.parent.mkdir(parents=True, exist_ok=True) diff --git a/tests/help/test_help.py b/tests/help/test_help.py index 1276c09f8b3..5a6240210f6 100644 --- a/tests/help/test_help.py +++ b/tests/help/test_help.py @@ -1,7 +1,9 @@ import asyncio +import importlib import time from unittest.mock import AsyncMock +import pytest from requests.exceptions import ConnectionError, ReadTimeout import cecli @@ -11,6 +13,8 @@ from cecli.io import InputOutput from cecli.models import Model +_has_llama_index = importlib.util.find_spec("llama_index") is not None + class TestHelp: @staticmethod @@ -90,10 +94,16 @@ async def async_setup_class(cls): # HelpCoder.run may or may not be called depending on help initialization # Don't assert it was called + @pytest.mark.skipif( + not _has_llama_index, reason="requires llama_index (install with cecli-dev[help])" + ) def test_init(self): help_inst = Help() assert help_inst.retriever is not None + @pytest.mark.skipif( + not _has_llama_index, reason="requires llama_index (install with cecli-dev[help])" + ) def test_ask_without_mock(self): help_instance = Help() question = "What is cecli?" diff --git a/tests/hooks/test_base.py b/tests/hooks/test_base.py index 9af8f9138ce..0300c3d2743 100644 --- a/tests/hooks/test_base.py +++ b/tests/hooks/test_base.py @@ -1,6 +1,5 @@ """Tests for base hook classes.""" - import pytest from cecli.hooks import BaseHook, CommandHook, HookType