diff --git a/assets/icons/ai_anthropic.svg b/assets/icons/ai_anthropic.svg deleted file mode 100644 index 12d731fb0b4438fcf6c263bd6c071bc8873823de..0000000000000000000000000000000000000000 --- a/assets/icons/ai_anthropic.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/assets/settings/default.json b/assets/settings/default.json index f687778d7bd7fc0f6d66404199c34fac8d77e7a8..83c01434635ee4e2d7c9d9b31f9d9d5716357ead 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -12,7 +12,7 @@ "theme": { "mode": "system", "light": "One Light", - "dark": "One Dark" + "dark": "One Dark", }, "icon_theme": "Zed (Default)", // The name of a base set of key bindings to use. @@ -29,7 +29,7 @@ // Features that can be globally enabled or disabled "features": { // Which edit prediction provider to use. - "edit_prediction_provider": "zed" + "edit_prediction_provider": "zed", }, // The name of a font to use for rendering text in the editor // ".ZedMono" currently aliases to Lilex @@ -69,7 +69,7 @@ // The OpenType features to enable for text in the UI "ui_font_features": { // Disable ligatures: - "calt": false + "calt": false, }, // The weight of the UI font in standard CSS units from 100 to 900. "ui_font_weight": 400, @@ -87,7 +87,7 @@ "border_size": 0.0, // Opacity of the inactive panes. 0 means transparent, 1 means opaque. // Values are clamped to the [0.0, 1.0] range. - "inactive_opacity": 1.0 + "inactive_opacity": 1.0, }, // Layout mode of the bottom dock. Defaults to "contained" // choices: contained, full, left_aligned, right_aligned @@ -103,12 +103,12 @@ "left_padding": 0.2, // The relative width of the right padding of the central pane from the // workspace when the centered layout is used. - "right_padding": 0.2 + "right_padding": 0.2, }, // Image viewer settings "image_viewer": { // The unit for image file sizes: "binary" (KiB, MiB) or decimal (KB, MB) - "unit": "binary" + "unit": "binary", }, // Determines the modifier to be used to add multiple cursors with the mouse. The open hover link mouse gestures will adapt such that it do not conflict with the multicursor modifier. // @@ -296,7 +296,7 @@ // When true, enables drag and drop text selection in buffer. "enabled": true, // The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. - "delay": 300 + "delay": 300, }, // What to do when go to definition yields no results. // @@ -400,14 +400,14 @@ // Visible characters used to render whitespace when show_whitespaces is enabled. "whitespace_map": { "space": "•", - "tab": "→" + "tab": "→", }, // Settings related to calls in Zed "calls": { // Join calls with the microphone live by default "mute_on_join": false, // Share your project when you are the first to join a channel - "share_on_join": false + "share_on_join": false, }, // Toolbar related settings "toolbar": { @@ -420,7 +420,7 @@ // Whether to show agent review buttons in the editor toolbar. "agent_review": true, // Whether to show code action buttons in the editor toolbar. - "code_actions": false + "code_actions": false, }, // Whether to allow windows to tab together based on the user’s tabbing preference (macOS only). "use_system_window_tabs": false, @@ -439,7 +439,7 @@ // Whether to show the sign in button in the titlebar. "show_sign_in": true, // Whether to show the menus in the titlebar. - "show_menus": false + "show_menus": false, }, "audio": { // Opt into the new audio system. @@ -472,7 +472,7 @@ // the future we will migrate by setting this to false // // You need to rejoin a call for this setting to apply - "experimental.legacy_audio_compatible": true + "experimental.legacy_audio_compatible": true, }, // Scrollbar related settings "scrollbar": { @@ -511,8 +511,8 @@ // When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings. "horizontal": true, // When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings. - "vertical": true - } + "vertical": true, + }, }, // Minimap related settings "minimap": { @@ -560,7 +560,7 @@ // 3. "gutter" or "none" to not highlight the current line in the minimap. "current_line_highlight": null, // Maximum number of columns to display in the minimap. - "max_width_columns": 80 + "max_width_columns": 80, }, // Enable middle-click paste on Linux. "middle_click_paste": true, @@ -583,7 +583,7 @@ // Whether to show fold buttons in the gutter. "folds": true, // Minimum number of characters to reserve space for in the gutter. - "min_line_number_digits": 4 + "min_line_number_digits": 4, }, "indent_guides": { // Whether to show indent guides in the editor. @@ -604,7 +604,7 @@ // // 1. "disabled" // 2. "indent_aware" - "background_coloring": "disabled" + "background_coloring": "disabled", }, // Whether the editor will scroll beyond the last line. "scroll_beyond_last_line": "one_page", @@ -623,7 +623,7 @@ "fast_scroll_sensitivity": 4.0, "sticky_scroll": { // Whether to stick scopes to the top of the editor. - "enabled": false + "enabled": false, }, "relative_line_numbers": "disabled", // If 'search_wrap' is disabled, search result do not wrap around the end of the file. @@ -641,7 +641,7 @@ // Whether to interpret the search query as a regular expression. "regex": false, // Whether to center the cursor on each search match when navigating. - "center_on_match": false + "center_on_match": false, }, // When to populate a new search's query based on the text under the cursor. // This setting can take the following three values: @@ -684,8 +684,8 @@ "shift": false, "alt": false, "platform": false, - "function": false - } + "function": false, + }, }, // Whether to resize all the panels in a dock when resizing the dock. // Can be a combination of "left", "right" and "bottom". @@ -733,7 +733,7 @@ // "always" // 5. Never show the scrollbar: // "never" - "show": null + "show": null, }, // Which files containing diagnostic errors/warnings to mark in the project panel. // This setting can take the following three values: @@ -756,7 +756,7 @@ // "always" // 2. Never show indent guides: // "never" - "show": "always" + "show": "always", }, // Sort order for entries in the project panel. // This setting can take three values: @@ -781,8 +781,8 @@ // Whether to automatically open files after pasting or duplicating them. "on_paste": true, // Whether to automatically open files dropped from external sources. - "on_drop": true - } + "on_drop": true, + }, }, "outline_panel": { // Whether to show the outline panel button in the status bar @@ -815,7 +815,7 @@ // "always" // 2. Never show indent guides: // "never" - "show": "always" + "show": "always", }, // Scrollbar-related settings "scrollbar": { @@ -832,11 +832,11 @@ // "always" // 5. Never show the scrollbar: // "never" - "show": null + "show": null, }, // Default depth to expand outline items in the current file. // Set to 0 to collapse all items that have children, 1 or higher to collapse items at that depth or deeper. - "expand_outlines_with_depth": 100 + "expand_outlines_with_depth": 100, }, "collaboration_panel": { // Whether to show the collaboration panel button in the status bar. @@ -844,7 +844,7 @@ // Where to dock the collaboration panel. Can be 'left' or 'right'. "dock": "left", // Default width of the collaboration panel. - "default_width": 240 + "default_width": 240, }, "git_panel": { // Whether to show the git panel button in the status bar. @@ -876,12 +876,12 @@ // Choices: always, auto, never, system // Default: inherits editor scrollbar settings // "show": null - } + }, }, "message_editor": { // Whether to automatically replace emoji shortcodes with emoji characters. // For example: typing `:wave:` gets replaced with `👋`. - "auto_replace_emoji_shortcode": true + "auto_replace_emoji_shortcode": true, }, "notification_panel": { // Whether to show the notification panel button in the status bar. @@ -889,7 +889,7 @@ // Where to dock the notification panel. Can be 'left' or 'right'. "dock": "right", // Default width of the notification panel. - "default_width": 380 + "default_width": 380, }, "agent": { // Whether the agent is enabled. @@ -911,7 +911,7 @@ // The provider to use. "provider": "zed.dev", // The model to use. - "model": "claude-sonnet-4" + "model": "claude-sonnet-4", }, // Additional parameters for language model requests. When making a request to a model, parameters will be taken // from the last entry in this list that matches the model's provider and name. In each entry, both provider @@ -966,8 +966,8 @@ "grep": true, "terminal": true, "thinking": true, - "web_search": true - } + "web_search": true, + }, }, "ask": { "name": "Ask", @@ -984,14 +984,14 @@ "open": true, "grep": true, "thinking": true, - "web_search": true - } + "web_search": true, + }, }, "minimal": { "name": "Minimal", "enable_all_context_servers": false, - "tools": {} - } + "tools": {}, + }, }, // Where to show notifications when the agent has either completed // its response, or else needs confirmation before it can run a @@ -1020,7 +1020,7 @@ // Minimum number of lines to display in the agent message editor. // // Default: 4 - "message_editor_min_lines": 4 + "message_editor_min_lines": 4, }, // Whether the screen sharing icon is shown in the os status bar. "show_call_status_icon": true, @@ -1055,7 +1055,7 @@ // Whether or not to show the navigation history buttons. "show_nav_history_buttons": true, // Whether or not to show the tab bar buttons. - "show_tab_bar_buttons": true + "show_tab_bar_buttons": true, }, // Settings related to the editor's tabs "tabs": { @@ -1094,7 +1094,7 @@ // "errors" // 3. Mark files with errors and warnings: // "all" - "show_diagnostics": "off" + "show_diagnostics": "off", }, // Settings related to preview tabs. "preview_tabs": { @@ -1115,7 +1115,7 @@ "enable_preview_file_from_code_navigation": true, // Whether to keep tabs in preview mode when code navigation is used to navigate away from them. // If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one. - "enable_keep_preview_on_code_navigation": false + "enable_keep_preview_on_code_navigation": false, }, // Settings related to the file finder. "file_finder": { @@ -1159,7 +1159,7 @@ // * "all": Use all gitignored files // * "indexed": Use only the files Zed had indexed // * "smart": Be smart and search for ignored when called from a gitignored worktree - "include_ignored": "smart" + "include_ignored": "smart", }, // Whether or not to remove any trailing whitespace from lines of a buffer // before saving it. @@ -1230,7 +1230,7 @@ // Send debug info like crash reports. "diagnostics": true, // Send anonymized usage data like what languages you're using Zed with. - "metrics": true + "metrics": true, }, // Whether to disable all AI features in Zed. // @@ -1264,7 +1264,7 @@ "enabled": true, // Minimum time to wait before pulling diagnostics from the language server(s). // 0 turns the debounce off. - "debounce_ms": 50 + "debounce_ms": 50, }, // Settings for inline diagnostics "inline": { @@ -1282,8 +1282,8 @@ "min_column": 0, // The minimum severity of the diagnostics to show inline. // Inherits editor's diagnostics' max severity settings when `null`. - "max_severity": null - } + "max_severity": null, + }, }, // Files or globs of files that will be excluded by Zed entirely. They will be skipped during file // scans, file searches, and not be displayed in the project file tree. Takes precedence over `file_scan_inclusions`. @@ -1297,7 +1297,7 @@ "**/.DS_Store", "**/Thumbs.db", "**/.classpath", - "**/.settings" + "**/.settings", ], // Files or globs of files that will be included by Zed, even when ignored by git. This is useful // for files that are not tracked by git, but are still important to your project. Note that globs @@ -1332,14 +1332,14 @@ // Whether or not to display the git commit summary on the same line. "show_commit_summary": false, // The minimum column number to show the inline blame information at - "min_column": 0 + "min_column": 0, }, "blame": { - "show_avatar": true + "show_avatar": true, }, // Control which information is shown in the branch picker. "branch_picker": { - "show_author_name": true + "show_author_name": true, }, // How git hunks are displayed visually in the editor. // This setting can take two values: @@ -1351,7 +1351,7 @@ "hunk_style": "staged_hollow", // Should the name or path be displayed first in the git view. // "path_style": "file_name_first" or "file_path_first" - "path_style": "file_name_first" + "path_style": "file_name_first", }, // The list of custom Git hosting providers. "git_hosting_providers": [ @@ -1385,7 +1385,7 @@ "**/secrets.yml", "**/.zed/settings.json", // zed project settings "/**/zed/settings.json", // zed user settings - "/**/zed/keymap.json" + "/**/zed/keymap.json", ], // When to show edit predictions previews in buffer. // This setting takes two possible values: @@ -1403,15 +1403,15 @@ "copilot": { "enterprise_uri": null, "proxy": null, - "proxy_no_verify": null + "proxy_no_verify": null, }, "codestral": { "model": null, - "max_tokens": null + "max_tokens": null, }, // Whether edit predictions are enabled when editing text threads in the agent panel. // This setting has no effect if globally disabled. - "enabled_in_text_threads": true + "enabled_in_text_threads": true, }, // Settings specific to journaling "journal": { @@ -1421,7 +1421,7 @@ // May take 2 values: // 1. hour12 // 2. hour24 - "hour_format": "hour12" + "hour_format": "hour12", }, // Status bar-related settings. "status_bar": { @@ -1432,7 +1432,7 @@ // Whether to show the cursor position button in the status bar. "cursor_position_button": true, // Whether to show active line endings button in the status bar. - "line_endings_button": false + "line_endings_button": false, }, // Settings specific to the terminal "terminal": { @@ -1553,8 +1553,8 @@ // Preferred Conda manager to use when activating Conda environments. // Values: "auto", "conda", "mamba", "micromamba" // Default: "auto" - "conda_manager": "auto" - } + "conda_manager": "auto", + }, }, "toolbar": { // Whether to display the terminal title in its toolbar's breadcrumbs. @@ -1562,7 +1562,7 @@ // // The shell running in the terminal needs to be configured to emit the title. // Example: `echo -e "\e]2;New Title\007";` - "breadcrumbs": false + "breadcrumbs": false, }, // Scrollbar-related settings "scrollbar": { @@ -1579,7 +1579,7 @@ // "always" // 5. Never show the scrollbar: // "never" - "show": null + "show": null, }, // Set the terminal's font size. If this option is not included, // the terminal will default to matching the buffer's font size. @@ -1660,12 +1660,12 @@ "# which may be followed by trailing punctuation", "[.,:)}\\]>]*", "# and always includes trailing whitespace or end of line", - "([ ]+|$)" - ] + "([ ]+|$)", + ], ], // Timeout for hover and Cmd-click path hyperlink discovery in milliseconds. Specifying a // timeout of `0` will disable path hyperlinking in terminal. - "path_hyperlink_timeout_ms": 1 + "path_hyperlink_timeout_ms": 1, }, "code_actions_on_format": {}, // Settings related to running tasks. @@ -1681,7 +1681,7 @@ // * Zed task from history (e.g. one-off task was spawned before) // // Default: true - "prefer_lsp": true + "prefer_lsp": true, }, // An object whose keys are language names, and whose values // are arrays of filenames or extensions of files that should @@ -1698,7 +1698,7 @@ "file_types": { "JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json", "tsconfig*.json"], "Markdown": [".rules", ".cursorrules", ".windsurfrules", ".clinerules"], - "Shell Script": [".env.*"] + "Shell Script": [".env.*"], }, // Settings for which version of Node.js and NPM to use when installing // language servers and Copilot. @@ -1714,14 +1714,14 @@ // `path`, but not `npm_path`, Zed will assume that `npm` is located at // `${path}/../npm`. "path": null, - "npm_path": null + "npm_path": null, }, // The extensions that Zed should automatically install on startup. // // If you don't want any of these extensions, add this field to your settings // and change the value to `false`. "auto_install_extensions": { - "html": true + "html": true, }, // The capabilities granted to extensions. // @@ -1729,7 +1729,7 @@ "granted_extension_capabilities": [ { "kind": "process:exec", "command": "*", "args": ["**"] }, { "kind": "download_file", "host": "*", "path": ["**"] }, - { "kind": "npm:install", "package": "*" } + { "kind": "npm:install", "package": "*" }, ], // Controls how completions are processed for this language. "completions": { @@ -1780,7 +1780,7 @@ // 4. "replace_suffix" // Behaves like `"replace"` if the text after the cursor is a suffix of the completion, and like // `"insert"` otherwise. - "lsp_insert_mode": "replace_suffix" + "lsp_insert_mode": "replace_suffix", }, // Different settings for specific languages. "languages": { @@ -1788,113 +1788,113 @@ "language_servers": ["astro-language-server", "..."], "prettier": { "allowed": true, - "plugins": ["prettier-plugin-astro"] - } + "plugins": ["prettier-plugin-astro"], + }, }, "Blade": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "C": { "format_on_save": "off", "use_on_type_format": false, "prettier": { - "allowed": false - } + "allowed": false, + }, }, "C++": { "format_on_save": "off", "use_on_type_format": false, "prettier": { - "allowed": false - } + "allowed": false, + }, }, "CSS": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "Dart": { - "tab_size": 2 + "tab_size": 2, }, "Diff": { "show_edit_predictions": false, "remove_trailing_whitespace_on_save": false, - "ensure_final_newline_on_save": false + "ensure_final_newline_on_save": false, }, "Elixir": { - "language_servers": ["elixir-ls", "!expert", "!next-ls", "!lexical", "..."] + "language_servers": ["elixir-ls", "!expert", "!next-ls", "!lexical", "..."], }, "Elm": { - "tab_size": 4 + "tab_size": 4, }, "Erlang": { - "language_servers": ["erlang-ls", "!elp", "..."] + "language_servers": ["erlang-ls", "!elp", "..."], }, "Git Commit": { "allow_rewrap": "anywhere", "soft_wrap": "editor_width", - "preferred_line_length": 72 + "preferred_line_length": 72, }, "Go": { "hard_tabs": true, "code_actions_on_format": { - "source.organizeImports": true + "source.organizeImports": true, }, - "debuggers": ["Delve"] + "debuggers": ["Delve"], }, "GraphQL": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "HEEX": { - "language_servers": ["elixir-ls", "!expert", "!next-ls", "!lexical", "..."] + "language_servers": ["elixir-ls", "!expert", "!next-ls", "!lexical", "..."], }, "HTML": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "HTML+ERB": { - "language_servers": ["herb", "!ruby-lsp", "..."] + "language_servers": ["herb", "!ruby-lsp", "..."], }, "Java": { "prettier": { "allowed": true, - "plugins": ["prettier-plugin-java"] - } + "plugins": ["prettier-plugin-java"], + }, }, "JavaScript": { "language_servers": ["!typescript-language-server", "vtsls", "..."], "prettier": { - "allowed": true - } + "allowed": true, + }, }, "JSON": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "JSONC": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "JS+ERB": { - "language_servers": ["!ruby-lsp", "..."] + "language_servers": ["!ruby-lsp", "..."], }, "Kotlin": { - "language_servers": ["!kotlin-language-server", "kotlin-lsp", "..."] + "language_servers": ["!kotlin-language-server", "kotlin-lsp", "..."], }, "LaTeX": { "formatter": "language_server", "language_servers": ["texlab", "..."], "prettier": { "allowed": true, - "plugins": ["prettier-plugin-latex"] - } + "plugins": ["prettier-plugin-latex"], + }, }, "Markdown": { "format_on_save": "off", @@ -1903,135 +1903,132 @@ "allow_rewrap": "anywhere", "soft_wrap": "editor_width", "prettier": { - "allowed": true - } + "allowed": true, + }, }, "PHP": { "language_servers": ["phpactor", "!intelephense", "!phptools", "..."], "prettier": { "allowed": true, "plugins": ["@prettier/plugin-php"], - "parser": "php" - } + "parser": "php", + }, }, "Plain Text": { "allow_rewrap": "anywhere", - "soft_wrap": "editor_width" + "soft_wrap": "editor_width", }, "Python": { "code_actions_on_format": { - "source.organizeImports.ruff": true + "source.organizeImports.ruff": true, }, "formatter": { "language_server": { - "name": "ruff" - } + "name": "ruff", + }, }, "debuggers": ["Debugpy"], - "language_servers": ["basedpyright", "ruff", "!ty", "!pyrefly", "!pyright", "!pylsp", "..."] + "language_servers": ["basedpyright", "ruff", "!ty", "!pyrefly", "!pyright", "!pylsp", "..."], }, "Ruby": { - "language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."] + "language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."], }, "Rust": { - "debuggers": ["CodeLLDB"] + "debuggers": ["CodeLLDB"], }, "SCSS": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "Starlark": { - "language_servers": ["starpls", "!buck2-lsp", "..."] + "language_servers": ["starpls", "!buck2-lsp", "..."], }, "Svelte": { "language_servers": ["svelte-language-server", "..."], "prettier": { "allowed": true, - "plugins": ["prettier-plugin-svelte"] - } + "plugins": ["prettier-plugin-svelte"], + }, }, "TSX": { "language_servers": ["!typescript-language-server", "vtsls", "..."], "prettier": { - "allowed": true - } + "allowed": true, + }, }, "Twig": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "TypeScript": { "language_servers": ["!typescript-language-server", "vtsls", "..."], "prettier": { - "allowed": true - } + "allowed": true, + }, }, "SystemVerilog": { "format_on_save": "off", "language_servers": ["!slang", "..."], - "use_on_type_format": false + "use_on_type_format": false, }, "Vue.js": { "language_servers": ["vue-language-server", "vtsls", "..."], "prettier": { - "allowed": true - } + "allowed": true, + }, }, "XML": { "prettier": { "allowed": true, - "plugins": ["@prettier/plugin-xml"] - } + "plugins": ["@prettier/plugin-xml"], + }, }, "YAML": { "prettier": { - "allowed": true - } + "allowed": true, + }, }, "YAML+ERB": { - "language_servers": ["!ruby-lsp", "..."] + "language_servers": ["!ruby-lsp", "..."], }, "Zig": { - "language_servers": ["zls", "..."] - } + "language_servers": ["zls", "..."], + }, }, // Different settings for specific language models. "language_models": { - "anthropic": { - "api_url": "https://api.anthropic.com" - }, "bedrock": {}, "google": { - "api_url": "https://generativelanguage.googleapis.com" + "api_url": "https://generativelanguage.googleapis.com", }, "ollama": { - "api_url": "http://localhost:11434" + "api_url": "http://localhost:11434", }, "openai": { - "api_url": "https://api.openai.com/v1" + "api_url": "https://api.openai.com/v1", }, "openai_compatible": {}, "open_router": { - "api_url": "https://openrouter.ai/api/v1" + "api_url": "https://openrouter.ai/api/v1", }, "lmstudio": { - "api_url": "http://localhost:1234/api/v0" + "api_url": "http://localhost:1234/api/v0", }, "deepseek": { - "api_url": "https://api.deepseek.com/v1" + "api_url": "https://api.deepseek.com/v1", }, "mistral": { - "api_url": "https://api.mistral.ai/v1" + "api_url": "https://api.mistral.ai/v1", }, "vercel": { - "api_url": "https://api.v0.dev/v1" + "api_url": "https://api.v0.dev/v1", }, "x_ai": { - "api_url": "https://api.x.ai/v1" + "api_url": "https://api.x.ai/v1", }, - "zed.dev": {} + "zed.dev": {}, }, "session": { // Whether or not to restore unsaved buffers on restart. @@ -2040,7 +2037,7 @@ // dirty files when closing the application. // // Default: true - "restore_unsaved_buffers": true + "restore_unsaved_buffers": true, }, // Zed's Prettier integration settings. // Allows to enable/disable formatting with Prettier @@ -2058,11 +2055,11 @@ // "singleQuote": true // Forces Prettier integration to use a specific parser name when formatting files with the language // when set to a non-empty string. - "parser": "" + "parser": "", }, // Settings for auto-closing of JSX tags. "jsx_tag_auto_close": { - "enabled": true + "enabled": true, }, // LSP Specific settings. "lsp": { @@ -2083,19 +2080,19 @@ // Specify the DAP name as a key here. "CodeLLDB": { "env": { - "RUST_LOG": "info" - } - } + "RUST_LOG": "info", + }, + }, }, // Common language server settings. "global_lsp_settings": { // Whether to show the LSP servers button in the status bar. - "button": true + "button": true, }, // Jupyter settings "jupyter": { "enabled": true, - "kernel_selections": {} + "kernel_selections": {}, // Specify the language name as the key and the kernel name as the value. // "kernel_selections": { // "python": "conda-base" @@ -2109,7 +2106,7 @@ "max_columns": 128, // Maximum number of lines to keep in REPL's scrollback buffer. // Clamped with [4, 256] range. - "max_lines": 32 + "max_lines": 32, }, // Vim settings "vim": { @@ -2123,7 +2120,7 @@ // Specify the mode as the key and the shape as the value. // The mode can be one of the following: "normal", "replace", "insert", "visual". // The shape can be one of the following: "block", "bar", "underline", "hollow". - "cursor_shape": {} + "cursor_shape": {}, }, // The server to connect to. If the environment variable // ZED_SERVER_URL is set, it will override this setting. @@ -2156,9 +2153,9 @@ "windows": { "languages": { "PHP": { - "language_servers": ["intelephense", "!phpactor", "!phptools", "..."] - } - } + "language_servers": ["intelephense", "!phpactor", "!phptools", "..."], + }, + }, }, // Whether to show full labels in line indicator or short ones // @@ -2217,7 +2214,7 @@ "dock": "bottom", "log_dap_communications": true, "format_dap_log_messages": true, - "button": true + "button": true, }, // Configures any number of settings profiles that are temporarily applied on // top of your existing user settings when selected from @@ -2244,5 +2241,5 @@ // Useful for filtering out noisy logs or enabling more verbose logging. // // Example: {"log": {"client": "warn"}} - "log": {} + "log": {}, } diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs index 041401418c427251a944fc39bb8ac83a0e22bc13..06e25253ee626ba76345d7a65b14f1fed0a04b8f 100644 --- a/crates/anthropic/src/anthropic.rs +++ b/crates/anthropic/src/anthropic.rs @@ -8,7 +8,7 @@ use futures::{AsyncBufReadExt, AsyncReadExt, StreamExt, io::BufReader, stream::B use http_client::http::{self, HeaderMap, HeaderValue}; use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest, StatusCode}; use serde::{Deserialize, Serialize}; -pub use settings::{AnthropicAvailableModel as AvailableModel, ModelMode}; +pub use settings::ModelMode; use strum::{EnumIter, EnumString}; use thiserror::Error; diff --git a/crates/icons/src/icons.rs b/crates/icons/src/icons.rs index d28e2c1030c3c2378aa7997f4799c503cee97105..ce4ba4d3fa2aa31e05a73c905fbfb8fc4e6d8a8d 100644 --- a/crates/icons/src/icons.rs +++ b/crates/icons/src/icons.rs @@ -9,7 +9,6 @@ use strum::{EnumIter, EnumString, IntoStaticStr}; #[strum(serialize_all = "snake_case")] pub enum IconName { Ai, - AiAnthropic, AiBedrock, AiClaude, AiDeepSeek, diff --git a/crates/language_models/src/language_models.rs b/crates/language_models/src/language_models.rs index 445c6e11daa849682e28aaec32530b19126cc735..8b8ca1e2912e2a65714ebf1f8d05d3d6a83ac3d4 100644 --- a/crates/language_models/src/language_models.rs +++ b/crates/language_models/src/language_models.rs @@ -14,7 +14,6 @@ pub mod provider; mod settings; pub mod ui; -use crate::provider::anthropic::AnthropicLanguageModelProvider; use crate::provider::bedrock::BedrockLanguageModelProvider; use crate::provider::cloud::CloudLanguageModelProvider; use crate::provider::copilot_chat::CopilotChatLanguageModelProvider; @@ -119,13 +118,6 @@ fn register_language_model_providers( )), cx, ); - registry.register_provider( - Arc::new(AnthropicLanguageModelProvider::new( - client.http_client(), - cx, - )), - cx, - ); registry.register_provider( Arc::new(OpenAiLanguageModelProvider::new(client.http_client(), cx)), cx, diff --git a/crates/language_models/src/provider.rs b/crates/language_models/src/provider.rs index d780195c66ec0d19c2b7d53e62b5e3629baa8a43..e585fc06f6b52313dd79dd3b307d0ab305817977 100644 --- a/crates/language_models/src/provider.rs +++ b/crates/language_models/src/provider.rs @@ -1,4 +1,3 @@ -pub mod anthropic; pub mod bedrock; pub mod cloud; pub mod copilot_chat; diff --git a/crates/language_models/src/provider/anthropic.rs b/crates/language_models/src/provider/anthropic.rs deleted file mode 100644 index 1affe38a08d22e2aaed8c1207513ce41a13b8e59..0000000000000000000000000000000000000000 --- a/crates/language_models/src/provider/anthropic.rs +++ /dev/null @@ -1,1045 +0,0 @@ -use anthropic::{ - ANTHROPIC_API_URL, AnthropicError, AnthropicModelMode, ContentDelta, Event, ResponseContent, - ToolResultContent, ToolResultPart, Usage, -}; -use anyhow::{Result, anyhow}; -use collections::{BTreeMap, HashMap}; -use futures::{FutureExt, Stream, StreamExt, future, future::BoxFuture, stream::BoxStream}; -use gpui::{AnyView, App, AsyncApp, Context, Entity, Task}; -use http_client::HttpClient; -use language_model::{ - AuthenticateError, ConfigurationViewTargetAgent, LanguageModel, - LanguageModelCacheConfiguration, LanguageModelCompletionError, LanguageModelId, - LanguageModelName, LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName, - LanguageModelProviderState, LanguageModelRequest, LanguageModelToolChoice, - LanguageModelToolResultContent, MessageContent, RateLimiter, Role, -}; -use language_model::{LanguageModelCompletionEvent, LanguageModelToolUse, StopReason}; -use settings::{Settings, SettingsStore}; -use std::pin::Pin; -use std::str::FromStr; -use std::sync::{Arc, LazyLock}; -use strum::IntoEnumIterator; -use ui::{List, prelude::*}; -use ui_input::InputField; -use util::ResultExt; -use zed_env_vars::{EnvVar, env_var}; - -use crate::api_key::ApiKeyState; -use crate::ui::{ConfiguredApiCard, InstructionListItem}; - -pub use settings::AnthropicAvailableModel as AvailableModel; - -const PROVIDER_ID: LanguageModelProviderId = language_model::ANTHROPIC_PROVIDER_ID; -const PROVIDER_NAME: LanguageModelProviderName = language_model::ANTHROPIC_PROVIDER_NAME; - -#[derive(Default, Clone, Debug, PartialEq)] -pub struct AnthropicSettings { - pub api_url: String, - /// Extend Zed's list of Anthropic models. - pub available_models: Vec, -} - -pub struct AnthropicLanguageModelProvider { - http_client: Arc, - state: Entity, -} - -const API_KEY_ENV_VAR_NAME: &str = "ANTHROPIC_API_KEY"; -static API_KEY_ENV_VAR: LazyLock = env_var!(API_KEY_ENV_VAR_NAME); - -pub struct State { - api_key_state: ApiKeyState, -} - -impl State { - fn is_authenticated(&self) -> bool { - self.api_key_state.has_key() - } - - fn set_api_key(&mut self, api_key: Option, cx: &mut Context) -> Task> { - let api_url = AnthropicLanguageModelProvider::api_url(cx); - self.api_key_state - .store(api_url, api_key, |this| &mut this.api_key_state, cx) - } - - fn authenticate(&mut self, cx: &mut Context) -> Task> { - let api_url = AnthropicLanguageModelProvider::api_url(cx); - self.api_key_state.load_if_needed( - api_url, - &API_KEY_ENV_VAR, - |this| &mut this.api_key_state, - cx, - ) - } -} - -impl AnthropicLanguageModelProvider { - pub fn new(http_client: Arc, cx: &mut App) -> Self { - let state = cx.new(|cx| { - cx.observe_global::(|this: &mut State, cx| { - let api_url = Self::api_url(cx); - this.api_key_state.handle_url_change( - api_url, - &API_KEY_ENV_VAR, - |this| &mut this.api_key_state, - cx, - ); - cx.notify(); - }) - .detach(); - State { - api_key_state: ApiKeyState::new(Self::api_url(cx)), - } - }); - - Self { http_client, state } - } - - fn create_language_model(&self, model: anthropic::Model) -> Arc { - Arc::new(AnthropicModel { - id: LanguageModelId::from(model.id().to_string()), - model, - state: self.state.clone(), - http_client: self.http_client.clone(), - request_limiter: RateLimiter::new(4), - }) - } - - fn settings(cx: &App) -> &AnthropicSettings { - &crate::AllLanguageModelSettings::get_global(cx).anthropic - } - - fn api_url(cx: &App) -> SharedString { - let api_url = &Self::settings(cx).api_url; - if api_url.is_empty() { - ANTHROPIC_API_URL.into() - } else { - SharedString::new(api_url.as_str()) - } - } -} - -impl LanguageModelProviderState for AnthropicLanguageModelProvider { - type ObservableEntity = State; - - fn observable_entity(&self) -> Option> { - Some(self.state.clone()) - } -} - -impl LanguageModelProvider for AnthropicLanguageModelProvider { - fn id(&self) -> LanguageModelProviderId { - PROVIDER_ID - } - - fn name(&self) -> LanguageModelProviderName { - PROVIDER_NAME - } - - fn icon(&self) -> IconName { - IconName::AiAnthropic - } - - fn default_model(&self, _cx: &App) -> Option> { - Some(self.create_language_model(anthropic::Model::default())) - } - - fn default_fast_model(&self, _cx: &App) -> Option> { - Some(self.create_language_model(anthropic::Model::default_fast())) - } - - fn recommended_models(&self, _cx: &App) -> Vec> { - [ - anthropic::Model::ClaudeSonnet4_5, - anthropic::Model::ClaudeSonnet4_5Thinking, - ] - .into_iter() - .map(|model| self.create_language_model(model)) - .collect() - } - - fn provided_models(&self, cx: &App) -> Vec> { - let mut models = BTreeMap::default(); - - // Add base models from anthropic::Model::iter() - for model in anthropic::Model::iter() { - if !matches!(model, anthropic::Model::Custom { .. }) { - models.insert(model.id().to_string(), model); - } - } - - // Override with available models from settings - for model in &AnthropicLanguageModelProvider::settings(cx).available_models { - models.insert( - model.name.clone(), - anthropic::Model::Custom { - name: model.name.clone(), - display_name: model.display_name.clone(), - max_tokens: model.max_tokens, - tool_override: model.tool_override.clone(), - cache_configuration: model.cache_configuration.as_ref().map(|config| { - anthropic::AnthropicModelCacheConfiguration { - max_cache_anchors: config.max_cache_anchors, - should_speculate: config.should_speculate, - min_total_token: config.min_total_token, - } - }), - max_output_tokens: model.max_output_tokens, - default_temperature: model.default_temperature, - extra_beta_headers: model.extra_beta_headers.clone(), - mode: model.mode.unwrap_or_default().into(), - }, - ); - } - - models - .into_values() - .map(|model| self.create_language_model(model)) - .collect() - } - - fn is_authenticated(&self, cx: &App) -> bool { - self.state.read(cx).is_authenticated() - } - - fn authenticate(&self, cx: &mut App) -> Task> { - self.state.update(cx, |state, cx| state.authenticate(cx)) - } - - fn configuration_view( - &self, - target_agent: ConfigurationViewTargetAgent, - window: &mut Window, - cx: &mut App, - ) -> AnyView { - cx.new(|cx| ConfigurationView::new(self.state.clone(), target_agent, window, cx)) - .into() - } - - fn reset_credentials(&self, cx: &mut App) -> Task> { - self.state - .update(cx, |state, cx| state.set_api_key(None, cx)) - } -} - -pub struct AnthropicModel { - id: LanguageModelId, - model: anthropic::Model, - state: Entity, - http_client: Arc, - request_limiter: RateLimiter, -} - -pub fn count_anthropic_tokens( - request: LanguageModelRequest, - cx: &App, -) -> BoxFuture<'static, Result> { - cx.background_spawn(async move { - let messages = request.messages; - let mut tokens_from_images = 0; - let mut string_messages = Vec::with_capacity(messages.len()); - - for message in messages { - use language_model::MessageContent; - - let mut string_contents = String::new(); - - for content in message.content { - match content { - MessageContent::Text(text) => { - string_contents.push_str(&text); - } - MessageContent::Thinking { .. } => { - // Thinking blocks are not included in the input token count. - } - MessageContent::RedactedThinking(_) => { - // Thinking blocks are not included in the input token count. - } - MessageContent::Image(image) => { - tokens_from_images += image.estimate_tokens(); - } - MessageContent::ToolUse(_tool_use) => { - // TODO: Estimate token usage from tool uses. - } - MessageContent::ToolResult(tool_result) => match &tool_result.content { - LanguageModelToolResultContent::Text(text) => { - string_contents.push_str(text); - } - LanguageModelToolResultContent::Image(image) => { - tokens_from_images += image.estimate_tokens(); - } - }, - } - } - - if !string_contents.is_empty() { - string_messages.push(tiktoken_rs::ChatCompletionRequestMessage { - role: match message.role { - Role::User => "user".into(), - Role::Assistant => "assistant".into(), - Role::System => "system".into(), - }, - content: Some(string_contents), - name: None, - function_call: None, - }); - } - } - - // Tiktoken doesn't yet support these models, so we manually use the - // same tokenizer as GPT-4. - tiktoken_rs::num_tokens_from_messages("gpt-4", &string_messages) - .map(|tokens| (tokens + tokens_from_images) as u64) - }) - .boxed() -} - -impl AnthropicModel { - fn stream_completion( - &self, - request: anthropic::Request, - cx: &AsyncApp, - ) -> BoxFuture< - 'static, - Result< - BoxStream<'static, Result>, - LanguageModelCompletionError, - >, - > { - let http_client = self.http_client.clone(); - - let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| { - let api_url = AnthropicLanguageModelProvider::api_url(cx); - (state.api_key_state.key(&api_url), api_url) - }) else { - return future::ready(Err(anyhow!("App state dropped").into())).boxed(); - }; - - let beta_headers = self.model.beta_headers(); - - async move { - let Some(api_key) = api_key else { - return Err(LanguageModelCompletionError::NoApiKey { - provider: PROVIDER_NAME, - }); - }; - let request = anthropic::stream_completion( - http_client.as_ref(), - &api_url, - &api_key, - request, - beta_headers, - ); - request.await.map_err(Into::into) - } - .boxed() - } -} - -impl LanguageModel for AnthropicModel { - fn id(&self) -> LanguageModelId { - self.id.clone() - } - - fn name(&self) -> LanguageModelName { - LanguageModelName::from(self.model.display_name().to_string()) - } - - fn provider_id(&self) -> LanguageModelProviderId { - PROVIDER_ID - } - - fn provider_name(&self) -> LanguageModelProviderName { - PROVIDER_NAME - } - - fn supports_tools(&self) -> bool { - true - } - - fn supports_images(&self) -> bool { - true - } - - fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool { - match choice { - LanguageModelToolChoice::Auto - | LanguageModelToolChoice::Any - | LanguageModelToolChoice::None => true, - } - } - - fn telemetry_id(&self) -> String { - format!("anthropic/{}", self.model.id()) - } - - fn api_key(&self, cx: &App) -> Option { - self.state.read_with(cx, |state, cx| { - let api_url = AnthropicLanguageModelProvider::api_url(cx); - state.api_key_state.key(&api_url).map(|key| key.to_string()) - }) - } - - fn max_token_count(&self) -> u64 { - self.model.max_token_count() - } - - fn max_output_tokens(&self) -> Option { - Some(self.model.max_output_tokens()) - } - - fn count_tokens( - &self, - request: LanguageModelRequest, - cx: &App, - ) -> BoxFuture<'static, Result> { - count_anthropic_tokens(request, cx) - } - - fn stream_completion( - &self, - request: LanguageModelRequest, - cx: &AsyncApp, - ) -> BoxFuture< - 'static, - Result< - BoxStream<'static, Result>, - LanguageModelCompletionError, - >, - > { - let request = into_anthropic( - request, - self.model.request_id().into(), - self.model.default_temperature(), - self.model.max_output_tokens(), - self.model.mode(), - ); - let request = self.stream_completion(request, cx); - let future = self.request_limiter.stream(async move { - let response = request.await?; - Ok(AnthropicEventMapper::new().map_stream(response)) - }); - async move { Ok(future.await?.boxed()) }.boxed() - } - - fn cache_configuration(&self) -> Option { - self.model - .cache_configuration() - .map(|config| LanguageModelCacheConfiguration { - max_cache_anchors: config.max_cache_anchors, - should_speculate: config.should_speculate, - min_total_token: config.min_total_token, - }) - } -} - -pub fn into_anthropic( - request: LanguageModelRequest, - model: String, - default_temperature: f32, - max_output_tokens: u64, - mode: AnthropicModelMode, -) -> anthropic::Request { - let mut new_messages: Vec = Vec::new(); - let mut system_message = String::new(); - - for message in request.messages { - if message.contents_empty() { - continue; - } - - match message.role { - Role::User | Role::Assistant => { - let mut anthropic_message_content: Vec = message - .content - .into_iter() - .filter_map(|content| match content { - MessageContent::Text(text) => { - let text = if text.chars().last().is_some_and(|c| c.is_whitespace()) { - text.trim_end().to_string() - } else { - text - }; - if !text.is_empty() { - Some(anthropic::RequestContent::Text { - text, - cache_control: None, - }) - } else { - None - } - } - MessageContent::Thinking { - text: thinking, - signature, - } => { - if !thinking.is_empty() { - Some(anthropic::RequestContent::Thinking { - thinking, - signature: signature.unwrap_or_default(), - cache_control: None, - }) - } else { - None - } - } - MessageContent::RedactedThinking(data) => { - if !data.is_empty() { - Some(anthropic::RequestContent::RedactedThinking { data }) - } else { - None - } - } - MessageContent::Image(image) => Some(anthropic::RequestContent::Image { - source: anthropic::ImageSource { - source_type: "base64".to_string(), - media_type: "image/png".to_string(), - data: image.source.to_string(), - }, - cache_control: None, - }), - MessageContent::ToolUse(tool_use) => { - Some(anthropic::RequestContent::ToolUse { - id: tool_use.id.to_string(), - name: tool_use.name.to_string(), - input: tool_use.input, - cache_control: None, - }) - } - MessageContent::ToolResult(tool_result) => { - Some(anthropic::RequestContent::ToolResult { - tool_use_id: tool_result.tool_use_id.to_string(), - is_error: tool_result.is_error, - content: match tool_result.content { - LanguageModelToolResultContent::Text(text) => { - ToolResultContent::Plain(text.to_string()) - } - LanguageModelToolResultContent::Image(image) => { - ToolResultContent::Multipart(vec![ToolResultPart::Image { - source: anthropic::ImageSource { - source_type: "base64".to_string(), - media_type: "image/png".to_string(), - data: image.source.to_string(), - }, - }]) - } - }, - cache_control: None, - }) - } - }) - .collect(); - let anthropic_role = match message.role { - Role::User => anthropic::Role::User, - Role::Assistant => anthropic::Role::Assistant, - Role::System => unreachable!("System role should never occur here"), - }; - if let Some(last_message) = new_messages.last_mut() - && last_message.role == anthropic_role - { - last_message.content.extend(anthropic_message_content); - continue; - } - - // Mark the last segment of the message as cached - if message.cache { - let cache_control_value = Some(anthropic::CacheControl { - cache_type: anthropic::CacheControlType::Ephemeral, - }); - for message_content in anthropic_message_content.iter_mut().rev() { - match message_content { - anthropic::RequestContent::RedactedThinking { .. } => { - // Caching is not possible, fallback to next message - } - anthropic::RequestContent::Text { cache_control, .. } - | anthropic::RequestContent::Thinking { cache_control, .. } - | anthropic::RequestContent::Image { cache_control, .. } - | anthropic::RequestContent::ToolUse { cache_control, .. } - | anthropic::RequestContent::ToolResult { cache_control, .. } => { - *cache_control = cache_control_value; - break; - } - } - } - } - - new_messages.push(anthropic::Message { - role: anthropic_role, - content: anthropic_message_content, - }); - } - Role::System => { - if !system_message.is_empty() { - system_message.push_str("\n\n"); - } - system_message.push_str(&message.string_contents()); - } - } - } - - anthropic::Request { - model, - messages: new_messages, - max_tokens: max_output_tokens, - system: if system_message.is_empty() { - None - } else { - Some(anthropic::StringOrContents::String(system_message)) - }, - thinking: if request.thinking_allowed - && let AnthropicModelMode::Thinking { budget_tokens } = mode - { - Some(anthropic::Thinking::Enabled { budget_tokens }) - } else { - None - }, - tools: request - .tools - .into_iter() - .map(|tool| anthropic::Tool { - name: tool.name, - description: tool.description, - input_schema: tool.input_schema, - }) - .collect(), - tool_choice: request.tool_choice.map(|choice| match choice { - LanguageModelToolChoice::Auto => anthropic::ToolChoice::Auto, - LanguageModelToolChoice::Any => anthropic::ToolChoice::Any, - LanguageModelToolChoice::None => anthropic::ToolChoice::None, - }), - metadata: None, - stop_sequences: Vec::new(), - temperature: request.temperature.or(Some(default_temperature)), - top_k: None, - top_p: None, - } -} - -pub struct AnthropicEventMapper { - tool_uses_by_index: HashMap, - usage: Usage, - stop_reason: StopReason, -} - -impl AnthropicEventMapper { - pub fn new() -> Self { - Self { - tool_uses_by_index: HashMap::default(), - usage: Usage::default(), - stop_reason: StopReason::EndTurn, - } - } - - pub fn map_stream( - mut self, - events: Pin>>>, - ) -> impl Stream> - { - events.flat_map(move |event| { - futures::stream::iter(match event { - Ok(event) => self.map_event(event), - Err(error) => vec![Err(error.into())], - }) - }) - } - - pub fn map_event( - &mut self, - event: Event, - ) -> Vec> { - match event { - Event::ContentBlockStart { - index, - content_block, - } => match content_block { - ResponseContent::Text { text } => { - vec![Ok(LanguageModelCompletionEvent::Text(text))] - } - ResponseContent::Thinking { thinking } => { - vec![Ok(LanguageModelCompletionEvent::Thinking { - text: thinking, - signature: None, - })] - } - ResponseContent::RedactedThinking { data } => { - vec![Ok(LanguageModelCompletionEvent::RedactedThinking { data })] - } - ResponseContent::ToolUse { id, name, .. } => { - self.tool_uses_by_index.insert( - index, - RawToolUse { - id, - name, - input_json: String::new(), - }, - ); - Vec::new() - } - }, - Event::ContentBlockDelta { index, delta } => match delta { - ContentDelta::TextDelta { text } => { - vec![Ok(LanguageModelCompletionEvent::Text(text))] - } - ContentDelta::ThinkingDelta { thinking } => { - vec![Ok(LanguageModelCompletionEvent::Thinking { - text: thinking, - signature: None, - })] - } - ContentDelta::SignatureDelta { signature } => { - vec![Ok(LanguageModelCompletionEvent::Thinking { - text: "".to_string(), - signature: Some(signature), - })] - } - ContentDelta::InputJsonDelta { partial_json } => { - if let Some(tool_use) = self.tool_uses_by_index.get_mut(&index) { - tool_use.input_json.push_str(&partial_json); - - // Try to convert invalid (incomplete) JSON into - // valid JSON that serde can accept, e.g. by closing - // unclosed delimiters. This way, we can update the - // UI with whatever has been streamed back so far. - if let Ok(input) = serde_json::Value::from_str( - &partial_json_fixer::fix_json(&tool_use.input_json), - ) { - return vec![Ok(LanguageModelCompletionEvent::ToolUse( - LanguageModelToolUse { - id: tool_use.id.clone().into(), - name: tool_use.name.clone().into(), - is_input_complete: false, - raw_input: tool_use.input_json.clone(), - input, - thought_signature: None, - }, - ))]; - } - } - vec![] - } - }, - Event::ContentBlockStop { index } => { - if let Some(tool_use) = self.tool_uses_by_index.remove(&index) { - let input_json = tool_use.input_json.trim(); - let input_value = if input_json.is_empty() { - Ok(serde_json::Value::Object(serde_json::Map::default())) - } else { - serde_json::Value::from_str(input_json) - }; - let event_result = match input_value { - Ok(input) => Ok(LanguageModelCompletionEvent::ToolUse( - LanguageModelToolUse { - id: tool_use.id.into(), - name: tool_use.name.into(), - is_input_complete: true, - input, - raw_input: tool_use.input_json.clone(), - thought_signature: None, - }, - )), - Err(json_parse_err) => { - Ok(LanguageModelCompletionEvent::ToolUseJsonParseError { - id: tool_use.id.into(), - tool_name: tool_use.name.into(), - raw_input: input_json.into(), - json_parse_error: json_parse_err.to_string(), - }) - } - }; - - vec![event_result] - } else { - Vec::new() - } - } - Event::MessageStart { message } => { - update_usage(&mut self.usage, &message.usage); - vec![ - Ok(LanguageModelCompletionEvent::UsageUpdate(convert_usage( - &self.usage, - ))), - Ok(LanguageModelCompletionEvent::StartMessage { - message_id: message.id, - }), - ] - } - Event::MessageDelta { delta, usage } => { - update_usage(&mut self.usage, &usage); - if let Some(stop_reason) = delta.stop_reason.as_deref() { - self.stop_reason = match stop_reason { - "end_turn" => StopReason::EndTurn, - "max_tokens" => StopReason::MaxTokens, - "tool_use" => StopReason::ToolUse, - "refusal" => StopReason::Refusal, - _ => { - log::error!("Unexpected anthropic stop_reason: {stop_reason}"); - StopReason::EndTurn - } - }; - } - vec![Ok(LanguageModelCompletionEvent::UsageUpdate( - convert_usage(&self.usage), - ))] - } - Event::MessageStop => { - vec![Ok(LanguageModelCompletionEvent::Stop(self.stop_reason))] - } - Event::Error { error } => { - vec![Err(error.into())] - } - _ => Vec::new(), - } - } -} - -struct RawToolUse { - id: String, - name: String, - input_json: String, -} - -/// Updates usage data by preferring counts from `new`. -fn update_usage(usage: &mut Usage, new: &Usage) { - if let Some(input_tokens) = new.input_tokens { - usage.input_tokens = Some(input_tokens); - } - if let Some(output_tokens) = new.output_tokens { - usage.output_tokens = Some(output_tokens); - } - if let Some(cache_creation_input_tokens) = new.cache_creation_input_tokens { - usage.cache_creation_input_tokens = Some(cache_creation_input_tokens); - } - if let Some(cache_read_input_tokens) = new.cache_read_input_tokens { - usage.cache_read_input_tokens = Some(cache_read_input_tokens); - } -} - -fn convert_usage(usage: &Usage) -> language_model::TokenUsage { - language_model::TokenUsage { - input_tokens: usage.input_tokens.unwrap_or(0), - output_tokens: usage.output_tokens.unwrap_or(0), - cache_creation_input_tokens: usage.cache_creation_input_tokens.unwrap_or(0), - cache_read_input_tokens: usage.cache_read_input_tokens.unwrap_or(0), - } -} - -struct ConfigurationView { - api_key_editor: Entity, - state: Entity, - load_credentials_task: Option>, - target_agent: ConfigurationViewTargetAgent, -} - -impl ConfigurationView { - const PLACEHOLDER_TEXT: &'static str = "sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; - - fn new( - state: Entity, - target_agent: ConfigurationViewTargetAgent, - window: &mut Window, - cx: &mut Context, - ) -> Self { - cx.observe(&state, |_, _, cx| { - cx.notify(); - }) - .detach(); - - let load_credentials_task = Some(cx.spawn({ - let state = state.clone(); - async move |this, cx| { - if let Some(task) = state - .update(cx, |state, cx| state.authenticate(cx)) - .log_err() - { - // We don't log an error, because "not signed in" is also an error. - let _ = task.await; - } - this.update(cx, |this, cx| { - this.load_credentials_task = None; - cx.notify(); - }) - .log_err(); - } - })); - - Self { - api_key_editor: cx.new(|cx| InputField::new(window, cx, Self::PLACEHOLDER_TEXT)), - state, - load_credentials_task, - target_agent, - } - } - - fn save_api_key(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context) { - let api_key = self.api_key_editor.read(cx).text(cx); - if api_key.is_empty() { - return; - } - - // url changes can cause the editor to be displayed again - self.api_key_editor - .update(cx, |editor, cx| editor.set_text("", window, cx)); - - let state = self.state.clone(); - cx.spawn_in(window, async move |_, cx| { - state - .update(cx, |state, cx| state.set_api_key(Some(api_key), cx))? - .await - }) - .detach_and_log_err(cx); - } - - fn reset_api_key(&mut self, window: &mut Window, cx: &mut Context) { - self.api_key_editor - .update(cx, |editor, cx| editor.set_text("", window, cx)); - - let state = self.state.clone(); - cx.spawn_in(window, async move |_, cx| { - state - .update(cx, |state, cx| state.set_api_key(None, cx))? - .await - }) - .detach_and_log_err(cx); - } - - fn should_render_editor(&self, cx: &mut Context) -> bool { - !self.state.read(cx).is_authenticated() - } -} - -impl Render for ConfigurationView { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let env_var_set = self.state.read(cx).api_key_state.is_from_env_var(); - let configured_card_label = if env_var_set { - format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable") - } else { - let api_url = AnthropicLanguageModelProvider::api_url(cx); - if api_url == ANTHROPIC_API_URL { - "API key configured".to_string() - } else { - format!("API key configured for {}", api_url) - } - }; - - if self.load_credentials_task.is_some() { - div() - .child(Label::new("Loading credentials...")) - .into_any_element() - } else if self.should_render_editor(cx) { - v_flex() - .size_full() - .on_action(cx.listener(Self::save_api_key)) - .child(Label::new(format!("To use {}, you need to add an API key. Follow these steps:", match &self.target_agent { - ConfigurationViewTargetAgent::ZedAgent => "Zed's agent with Anthropic".into(), - ConfigurationViewTargetAgent::Other(agent) => agent.clone(), - }))) - .child( - List::new() - .child( - InstructionListItem::new( - "Create one by visiting", - Some("Anthropic's settings"), - Some("https://console.anthropic.com/settings/keys") - ) - ) - .child( - InstructionListItem::text_only("Paste your API key below and hit enter to start using the agent") - ) - ) - .child(self.api_key_editor.clone()) - .child( - Label::new( - format!("You can also assign the {API_KEY_ENV_VAR_NAME} environment variable and restart Zed."), - ) - .size(LabelSize::Small) - .color(Color::Muted), - ) - .into_any_element() - } else { - ConfiguredApiCard::new(configured_card_label) - .disabled(env_var_set) - .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))) - .when(env_var_set, |this| { - this.tooltip_label(format!( - "To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable." - )) - }) - .into_any_element() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use anthropic::AnthropicModelMode; - use language_model::{LanguageModelRequestMessage, MessageContent}; - - #[test] - fn test_cache_control_only_on_last_segment() { - let request = LanguageModelRequest { - messages: vec![LanguageModelRequestMessage { - role: Role::User, - content: vec![ - MessageContent::Text("Some prompt".to_string()), - MessageContent::Image(language_model::LanguageModelImage::empty()), - MessageContent::Image(language_model::LanguageModelImage::empty()), - MessageContent::Image(language_model::LanguageModelImage::empty()), - MessageContent::Image(language_model::LanguageModelImage::empty()), - ], - cache: true, - reasoning_details: None, - }], - thread_id: None, - prompt_id: None, - intent: None, - mode: None, - stop: vec![], - temperature: None, - tools: vec![], - tool_choice: None, - thinking_allowed: true, - }; - - let anthropic_request = into_anthropic( - request, - "claude-3-5-sonnet".to_string(), - 0.7, - 4096, - AnthropicModelMode::Default, - ); - - assert_eq!(anthropic_request.messages.len(), 1); - - let message = &anthropic_request.messages[0]; - assert_eq!(message.content.len(), 5); - - assert!(matches!( - message.content[0], - anthropic::RequestContent::Text { - cache_control: None, - .. - } - )); - for i in 1..3 { - assert!(matches!( - message.content[i], - anthropic::RequestContent::Image { - cache_control: None, - .. - } - )); - } - - assert!(matches!( - message.content[4], - anthropic::RequestContent::Image { - cache_control: Some(anthropic::CacheControl { - cache_type: anthropic::CacheControlType::Ephemeral, - }), - .. - } - )); - } -} diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index a19a427dbacb32883b1877888ec04899a2b8d427..8f08ddacee91e132663c2d6dcc28ff255965a2e2 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -1,5 +1,8 @@ use ai_onboarding::YoungAccountBanner; -use anthropic::AnthropicModelMode; +use anthropic::{ + AnthropicModelMode, ContentDelta, Event, ResponseContent, ToolResultContent, ToolResultPart, + Usage, +}; use anyhow::{Context as _, Result, anyhow}; use chrono::{DateTime, Utc}; use client::{Client, ModelRequestUsage, UserStore, zed_urls}; @@ -23,8 +26,9 @@ use language_model::{ LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelId, LanguageModelName, LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName, LanguageModelProviderState, LanguageModelRequest, LanguageModelToolChoice, - LanguageModelToolSchemaFormat, LlmApiToken, ModelRequestLimitReachedError, - PaymentRequiredError, RateLimiter, RefreshLlmTokenListener, + LanguageModelToolResultContent, LanguageModelToolSchemaFormat, LanguageModelToolUse, + LanguageModelToolUseId, LlmApiToken, MessageContent, ModelRequestLimitReachedError, + PaymentRequiredError, RateLimiter, RefreshLlmTokenListener, Role, StopReason, }; use release_channel::AppVersion; use schemars::JsonSchema; @@ -42,7 +46,6 @@ use thiserror::Error; use ui::{TintColor, prelude::*}; use util::{ResultExt as _, maybe}; -use crate::provider::anthropic::{AnthropicEventMapper, count_anthropic_tokens, into_anthropic}; use crate::provider::google::{GoogleEventMapper, into_google}; use crate::provider::open_ai::{OpenAiEventMapper, count_open_ai_tokens, into_open_ai}; use crate::provider::x_ai::count_xai_tokens; @@ -1394,3 +1397,435 @@ mod tests { } } } + +fn count_anthropic_tokens( + request: LanguageModelRequest, + cx: &App, +) -> BoxFuture<'static, Result> { + use gpui::AppContext as _; + cx.background_spawn(async move { + let messages = request.messages; + let mut tokens_from_images = 0; + let mut string_messages = Vec::with_capacity(messages.len()); + + for message in messages { + let mut string_contents = String::new(); + + for content in message.content { + match content { + MessageContent::Text(text) => { + string_contents.push_str(&text); + } + MessageContent::Thinking { .. } => {} + MessageContent::RedactedThinking(_) => {} + MessageContent::Image(image) => { + tokens_from_images += image.estimate_tokens(); + } + MessageContent::ToolUse(_tool_use) => {} + MessageContent::ToolResult(tool_result) => match &tool_result.content { + LanguageModelToolResultContent::Text(text) => { + string_contents.push_str(text); + } + LanguageModelToolResultContent::Image(image) => { + tokens_from_images += image.estimate_tokens(); + } + }, + } + } + + if !string_contents.is_empty() { + string_messages.push(tiktoken_rs::ChatCompletionRequestMessage { + role: match message.role { + Role::User => "user".into(), + Role::Assistant => "assistant".into(), + Role::System => "system".into(), + }, + content: Some(string_contents), + name: None, + function_call: None, + }); + } + } + + tiktoken_rs::num_tokens_from_messages("gpt-4", &string_messages) + .map(|tokens| (tokens + tokens_from_images) as u64) + }) + .boxed() +} + +fn into_anthropic( + request: LanguageModelRequest, + model: String, + default_temperature: f32, + max_output_tokens: u64, + mode: AnthropicModelMode, +) -> anthropic::Request { + let mut new_messages: Vec = Vec::new(); + let mut system_message = String::new(); + + for message in request.messages { + if message.contents_empty() { + continue; + } + + match message.role { + Role::User | Role::Assistant => { + let mut anthropic_message_content: Vec = message + .content + .into_iter() + .filter_map(|content| match content { + MessageContent::Text(text) => { + let text = if text.chars().last().is_some_and(|c| c.is_whitespace()) { + text.trim_end().to_string() + } else { + text + }; + if !text.is_empty() { + Some(anthropic::RequestContent::Text { + text, + cache_control: None, + }) + } else { + None + } + } + MessageContent::Thinking { + text: thinking, + signature, + } => { + if !thinking.is_empty() { + Some(anthropic::RequestContent::Thinking { + thinking, + signature: signature.unwrap_or_default(), + cache_control: None, + }) + } else { + None + } + } + MessageContent::RedactedThinking(data) => { + if !data.is_empty() { + Some(anthropic::RequestContent::RedactedThinking { data }) + } else { + None + } + } + MessageContent::Image(image) => Some(anthropic::RequestContent::Image { + source: anthropic::ImageSource { + source_type: "base64".to_string(), + media_type: "image/png".to_string(), + data: image.source.to_string(), + }, + cache_control: None, + }), + MessageContent::ToolUse(tool_use) => { + Some(anthropic::RequestContent::ToolUse { + id: tool_use.id.to_string(), + name: tool_use.name.to_string(), + input: tool_use.input, + cache_control: None, + }) + } + MessageContent::ToolResult(tool_result) => { + Some(anthropic::RequestContent::ToolResult { + tool_use_id: tool_result.tool_use_id.to_string(), + is_error: tool_result.is_error, + content: match tool_result.content { + LanguageModelToolResultContent::Text(text) => { + ToolResultContent::Plain(text.to_string()) + } + LanguageModelToolResultContent::Image(image) => { + ToolResultContent::Multipart(vec![ToolResultPart::Image { + source: anthropic::ImageSource { + source_type: "base64".to_string(), + media_type: "image/png".to_string(), + data: image.source.to_string(), + }, + }]) + } + }, + cache_control: None, + }) + } + }) + .collect(); + let anthropic_role = match message.role { + Role::User => anthropic::Role::User, + Role::Assistant => anthropic::Role::Assistant, + Role::System => unreachable!("System role should never occur here"), + }; + if let Some(last_message) = new_messages.last_mut() + && last_message.role == anthropic_role + { + last_message.content.extend(anthropic_message_content); + continue; + } + + if message.cache { + let cache_control_value = Some(anthropic::CacheControl { + cache_type: anthropic::CacheControlType::Ephemeral, + }); + for message_content in anthropic_message_content.iter_mut().rev() { + match message_content { + anthropic::RequestContent::RedactedThinking { .. } => {} + anthropic::RequestContent::Text { cache_control, .. } + | anthropic::RequestContent::Thinking { cache_control, .. } + | anthropic::RequestContent::Image { cache_control, .. } + | anthropic::RequestContent::ToolUse { cache_control, .. } + | anthropic::RequestContent::ToolResult { cache_control, .. } => { + *cache_control = cache_control_value; + break; + } + } + } + } + + new_messages.push(anthropic::Message { + role: anthropic_role, + content: anthropic_message_content, + }); + } + Role::System => { + if !system_message.is_empty() { + system_message.push_str("\n\n"); + } + system_message.push_str(&message.string_contents()); + } + } + } + + anthropic::Request { + model, + messages: new_messages, + max_tokens: max_output_tokens, + system: if system_message.is_empty() { + None + } else { + Some(anthropic::StringOrContents::String(system_message)) + }, + thinking: if request.thinking_allowed + && let AnthropicModelMode::Thinking { budget_tokens } = mode + { + Some(anthropic::Thinking::Enabled { budget_tokens }) + } else { + None + }, + tools: request + .tools + .into_iter() + .map(|tool| anthropic::Tool { + name: tool.name, + description: tool.description, + input_schema: tool.input_schema, + }) + .collect(), + tool_choice: request.tool_choice.map(|choice| match choice { + LanguageModelToolChoice::Auto => anthropic::ToolChoice::Auto, + LanguageModelToolChoice::Any => anthropic::ToolChoice::Any, + LanguageModelToolChoice::None => anthropic::ToolChoice::None, + }), + metadata: None, + stop_sequences: Vec::new(), + temperature: request.temperature.or(Some(default_temperature)), + top_k: None, + top_p: None, + } +} + +struct AnthropicEventMapper { + tool_uses_by_index: collections::HashMap, + usage: Usage, + stop_reason: StopReason, +} + +impl AnthropicEventMapper { + fn new() -> Self { + Self { + tool_uses_by_index: collections::HashMap::default(), + usage: Usage::default(), + stop_reason: StopReason::EndTurn, + } + } + + fn map_event( + &mut self, + event: Event, + ) -> Vec> { + match event { + Event::ContentBlockStart { + index, + content_block, + } => match content_block { + ResponseContent::Text { text } => { + vec![Ok(LanguageModelCompletionEvent::Text(text))] + } + ResponseContent::Thinking { thinking } => { + vec![Ok(LanguageModelCompletionEvent::Thinking { + text: thinking, + signature: None, + })] + } + ResponseContent::RedactedThinking { data } => { + vec![Ok(LanguageModelCompletionEvent::RedactedThinking { data })] + } + ResponseContent::ToolUse { id, name, .. } => { + self.tool_uses_by_index.insert( + index, + RawToolUse { + id, + name, + input_json: String::new(), + }, + ); + Vec::new() + } + }, + Event::ContentBlockDelta { index, delta } => match delta { + ContentDelta::TextDelta { text } => { + vec![Ok(LanguageModelCompletionEvent::Text(text))] + } + ContentDelta::ThinkingDelta { thinking } => { + vec![Ok(LanguageModelCompletionEvent::Thinking { + text: thinking, + signature: None, + })] + } + ContentDelta::SignatureDelta { signature } => { + vec![Ok(LanguageModelCompletionEvent::Thinking { + text: "".to_string(), + signature: Some(signature), + })] + } + ContentDelta::InputJsonDelta { partial_json } => { + if let Some(tool_use) = self.tool_uses_by_index.get_mut(&index) { + tool_use.input_json.push_str(&partial_json); + + let event = serde_json::from_str::(&tool_use.input_json) + .ok() + .and_then(|input| { + let input_json_roundtripped = + serde_json::to_string(&input).ok()?.to_string(); + + if !tool_use.input_json.starts_with(&input_json_roundtripped) { + return None; + } + + Some(LanguageModelCompletionEvent::ToolUse( + LanguageModelToolUse { + id: LanguageModelToolUseId::from(tool_use.id.clone()), + name: tool_use.name.clone().into(), + raw_input: tool_use.input_json.clone(), + input, + is_input_complete: false, + thought_signature: None, + }, + )) + }); + + if let Some(event) = event { + vec![Ok(event)] + } else { + Vec::new() + } + } else { + Vec::new() + } + } + }, + Event::ContentBlockStop { index } => { + if let Some(tool_use) = self.tool_uses_by_index.remove(&index) { + let event_result = match serde_json::from_str(&tool_use.input_json) { + Ok(input) => Ok(LanguageModelCompletionEvent::ToolUse( + LanguageModelToolUse { + id: LanguageModelToolUseId::from(tool_use.id), + name: tool_use.name.into(), + raw_input: tool_use.input_json, + input, + is_input_complete: true, + thought_signature: None, + }, + )), + Err(json_parse_err) => { + Ok(LanguageModelCompletionEvent::ToolUseJsonParseError { + id: LanguageModelToolUseId::from(tool_use.id), + tool_name: tool_use.name.into(), + raw_input: tool_use.input_json.into(), + json_parse_error: json_parse_err.to_string(), + }) + } + }; + + vec![event_result] + } else { + Vec::new() + } + } + Event::MessageStart { message } => { + update_anthropic_usage(&mut self.usage, &message.usage); + vec![ + Ok(LanguageModelCompletionEvent::UsageUpdate( + convert_anthropic_usage(&self.usage), + )), + Ok(LanguageModelCompletionEvent::StartMessage { + message_id: message.id, + }), + ] + } + Event::MessageDelta { delta, usage } => { + update_anthropic_usage(&mut self.usage, &usage); + if let Some(stop_reason) = delta.stop_reason.as_deref() { + self.stop_reason = match stop_reason { + "end_turn" => StopReason::EndTurn, + "max_tokens" => StopReason::MaxTokens, + "tool_use" => StopReason::ToolUse, + "refusal" => StopReason::Refusal, + _ => { + log::error!("Unexpected anthropic stop_reason: {stop_reason}"); + StopReason::EndTurn + } + }; + } + vec![Ok(LanguageModelCompletionEvent::UsageUpdate( + convert_anthropic_usage(&self.usage), + ))] + } + Event::MessageStop => { + vec![Ok(LanguageModelCompletionEvent::Stop(self.stop_reason))] + } + Event::Error { error } => { + vec![Err(error.into())] + } + _ => Vec::new(), + } + } +} + +struct RawToolUse { + id: String, + name: String, + input_json: String, +} + +fn update_anthropic_usage(usage: &mut Usage, new: &Usage) { + if let Some(input_tokens) = new.input_tokens { + usage.input_tokens = Some(input_tokens); + } + if let Some(output_tokens) = new.output_tokens { + usage.output_tokens = Some(output_tokens); + } + if let Some(cache_creation_input_tokens) = new.cache_creation_input_tokens { + usage.cache_creation_input_tokens = Some(cache_creation_input_tokens); + } + if let Some(cache_read_input_tokens) = new.cache_read_input_tokens { + usage.cache_read_input_tokens = Some(cache_read_input_tokens); + } +} + +fn convert_anthropic_usage(usage: &Usage) -> language_model::TokenUsage { + language_model::TokenUsage { + input_tokens: usage.input_tokens.unwrap_or(0), + output_tokens: usage.output_tokens.unwrap_or(0), + cache_creation_input_tokens: usage.cache_creation_input_tokens.unwrap_or(0), + cache_read_input_tokens: usage.cache_read_input_tokens.unwrap_or(0), + } +} diff --git a/crates/language_models/src/settings.rs b/crates/language_models/src/settings.rs index 43a8e7334a744c84d6edfae3ffc97115eb8f51b2..15a3c936705194891ad8fbbdc4b369e27d64b261 100644 --- a/crates/language_models/src/settings.rs +++ b/crates/language_models/src/settings.rs @@ -4,16 +4,14 @@ use collections::HashMap; use settings::RegisterSetting; use crate::provider::{ - anthropic::AnthropicSettings, bedrock::AmazonBedrockSettings, cloud::ZedDotDevSettings, - deepseek::DeepSeekSettings, google::GoogleSettings, lmstudio::LmStudioSettings, - mistral::MistralSettings, ollama::OllamaSettings, open_ai::OpenAiSettings, - open_ai_compatible::OpenAiCompatibleSettings, open_router::OpenRouterSettings, - vercel::VercelSettings, x_ai::XAiSettings, + bedrock::AmazonBedrockSettings, cloud::ZedDotDevSettings, deepseek::DeepSeekSettings, + google::GoogleSettings, lmstudio::LmStudioSettings, mistral::MistralSettings, + ollama::OllamaSettings, open_ai::OpenAiSettings, open_ai_compatible::OpenAiCompatibleSettings, + open_router::OpenRouterSettings, vercel::VercelSettings, x_ai::XAiSettings, }; #[derive(Debug, RegisterSetting)] pub struct AllLanguageModelSettings { - pub anthropic: AnthropicSettings, pub bedrock: AmazonBedrockSettings, pub deepseek: DeepSeekSettings, pub google: GoogleSettings, @@ -33,7 +31,6 @@ impl settings::Settings for AllLanguageModelSettings { fn from_settings(content: &settings::SettingsContent) -> Self { let language_models = content.language_models.clone().unwrap(); - let anthropic = language_models.anthropic.unwrap(); let bedrock = language_models.bedrock.unwrap(); let deepseek = language_models.deepseek.unwrap(); let google = language_models.google.unwrap(); @@ -47,10 +44,6 @@ impl settings::Settings for AllLanguageModelSettings { let x_ai = language_models.x_ai.unwrap(); let zed_dot_dev = language_models.zed_dot_dev.unwrap(); Self { - anthropic: AnthropicSettings { - api_url: anthropic.api_url.unwrap(), - available_models: anthropic.available_models.unwrap_or_default(), - }, bedrock: AmazonBedrockSettings { available_models: bedrock.available_models.unwrap_or_default(), region: bedrock.region, diff --git a/crates/settings/src/settings_content/agent.rs b/crates/settings/src/settings_content/agent.rs index 2ea9f0cd5788f3312061ec8ffef2a728403463ac..e875c95f1c89b68cbb226f6c32734d82846d3b5d 100644 --- a/crates/settings/src/settings_content/agent.rs +++ b/crates/settings/src/settings_content/agent.rs @@ -255,7 +255,6 @@ impl JsonSchema for LanguageModelProviderSetting { "type": "string", "enum": [ "amazon-bedrock", - "anthropic", "copilot_chat", "deepseek", "google", diff --git a/crates/settings/src/settings_content/language_model.rs b/crates/settings/src/settings_content/language_model.rs index 48f5a463a4b8d896885d9ba5b7d804d16ecb5b6b..f99e1687130d8046b812700cbb2dc33b00f8d881 100644 --- a/crates/settings/src/settings_content/language_model.rs +++ b/crates/settings/src/settings_content/language_model.rs @@ -8,7 +8,6 @@ use std::sync::Arc; #[with_fallible_options] #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)] pub struct AllLanguageModelSettingsContent { - pub anthropic: Option, pub bedrock: Option, pub deepseek: Option, pub google: Option, @@ -24,35 +23,6 @@ pub struct AllLanguageModelSettingsContent { pub zed_dot_dev: Option, } -#[with_fallible_options] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)] -pub struct AnthropicSettingsContent { - pub api_url: Option, - pub available_models: Option>, -} - -#[with_fallible_options] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)] -pub struct AnthropicAvailableModel { - /// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-latest, claude-3-opus-20240229, etc - pub name: String, - /// The model's name in Zed's UI, such as in the model selector dropdown menu in the assistant panel. - pub display_name: Option, - /// The model's context window size. - pub max_tokens: u64, - /// A model `name` to substitute when calling tools, in case the primary model doesn't support tool calling. - pub tool_override: Option, - /// Configuration of Anthropic's caching API. - pub cache_configuration: Option, - pub max_output_tokens: Option, - #[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")] - pub default_temperature: Option, - #[serde(default)] - pub extra_beta_headers: Vec, - /// The model's mode (e.g. thinking) - pub mode: Option, -} - #[with_fallible_options] #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)] pub struct AmazonBedrockSettingsContent { diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 477885a4537580aaf562aa596c1a06cae1c65bc8..63ad054bfae1a2388d4283546842a0b246ab95d9 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -2626,9 +2626,6 @@ These values take in the same options as the root-level settings with the same n ```json [settings] { "language_models": { - "anthropic": { - "api_url": "https://api.anthropic.com" - }, "google": { "api_url": "https://generativelanguage.googleapis.com" },