Compare commits
2 Commits
new-provid
...
refactor-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ac098ce0a | ||
|
|
8ff95c153e |
97
Cargo.lock
generated
97
Cargo.lock
generated
@@ -2130,15 +2130,30 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bit-vec 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
@@ -2317,9 +2332,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "borrow-or-share"
|
||||
version = "0.2.4"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
|
||||
checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32"
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
@@ -5904,12 +5919,9 @@ dependencies = [
|
||||
"async-trait",
|
||||
"client",
|
||||
"collections",
|
||||
"credentials_provider",
|
||||
"criterion",
|
||||
"ctor",
|
||||
"dap",
|
||||
"dirs 4.0.0",
|
||||
"editor",
|
||||
"extension",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
@@ -5918,11 +5930,8 @@ dependencies = [
|
||||
"http_client",
|
||||
"language",
|
||||
"language_extension",
|
||||
"language_model",
|
||||
"log",
|
||||
"lsp",
|
||||
"markdown",
|
||||
"menu",
|
||||
"moka",
|
||||
"node_runtime",
|
||||
"parking_lot",
|
||||
@@ -5937,14 +5946,12 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_json_lenient",
|
||||
"settings",
|
||||
"smol",
|
||||
"task",
|
||||
"telemetry",
|
||||
"tempfile",
|
||||
"theme",
|
||||
"theme_extension",
|
||||
"toml 0.8.23",
|
||||
"ui",
|
||||
"url",
|
||||
"util",
|
||||
"wasmparser 0.221.3",
|
||||
@@ -6001,11 +6008,22 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.16.2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f"
|
||||
checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-set 0.5.3",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
|
||||
dependencies = [
|
||||
"bit-set 0.8.0",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
@@ -6227,9 +6245,9 @@ checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
|
||||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.4.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e"
|
||||
checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5"
|
||||
dependencies = [
|
||||
"borrow-or-share",
|
||||
"ref-cast",
|
||||
@@ -7525,17 +7543,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.8.4"
|
||||
@@ -8625,21 +8632,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "0.37.4"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73c9ffb2b5c56d58030e1b532d8e8389da94590515f118cf35b5cb68e4764a7e"
|
||||
checksum = "f1b46a0365a611fbf1d2143104dcf910aada96fafd295bab16c60b802bf6fa1d"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"base64 0.22.1",
|
||||
"bytecount",
|
||||
"data-encoding",
|
||||
"email_address",
|
||||
"fancy-regex",
|
||||
"fancy-regex 0.14.0",
|
||||
"fraction",
|
||||
"getrandom 0.3.4",
|
||||
"idna",
|
||||
"itoa",
|
||||
"num-cmp",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"referencing",
|
||||
"regex",
|
||||
@@ -8647,7 +8654,6 @@ dependencies = [
|
||||
"reqwest 0.12.24",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"unicode-general-category",
|
||||
"uuid-simd",
|
||||
]
|
||||
|
||||
@@ -8919,7 +8925,6 @@ dependencies = [
|
||||
"credentials_provider",
|
||||
"deepseek",
|
||||
"editor",
|
||||
"extension",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"google_ai",
|
||||
@@ -10197,7 +10202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
"bit-set 0.8.0",
|
||||
"bitflags 2.9.4",
|
||||
"cfg_aliases 0.2.1",
|
||||
"codespan-reporting 0.12.0",
|
||||
@@ -13053,7 +13058,7 @@ dependencies = [
|
||||
"dap",
|
||||
"dap_adapters",
|
||||
"extension",
|
||||
"fancy-regex",
|
||||
"fancy-regex 0.14.0",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
@@ -13924,14 +13929,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.37.4"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4283168a506f0dcbdce31c9f9cce3129c924da4c6bca46e46707fcb746d2d70c"
|
||||
checksum = "c8eff4fa778b5c2a57e85c5f2fe3a709c52f0e60d23146e2151cbef5893f420e"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"fluent-uri",
|
||||
"getrandom 0.3.4",
|
||||
"hashbrown 0.16.1",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"serde_json",
|
||||
@@ -17125,7 +17129,7 @@ dependencies = [
|
||||
"alacritty_terminal",
|
||||
"anyhow",
|
||||
"collections",
|
||||
"fancy-regex",
|
||||
"fancy-regex 0.14.0",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"itertools 0.14.0",
|
||||
@@ -17359,12 +17363,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tiktoken-rs"
|
||||
version = "0.9.1"
|
||||
source = "git+https://github.com/zed-industries/tiktoken-rs?rev=2570c4387a8505fb8f1d3f3557454b474f1e8271#2570c4387a8505fb8f1d3f3557454b474f1e8271"
|
||||
source = "git+https://github.com/zed-industries/tiktoken-rs?rev=7249f999c5fdf9bf3cc5c288c964454e4dac0c00#7249f999c5fdf9bf3cc5c288c964454e4dac0c00"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bstr",
|
||||
"fancy-regex",
|
||||
"fancy-regex 0.13.0",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
@@ -18496,12 +18500,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
@@ -18736,6 +18734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8"
|
||||
dependencies = [
|
||||
"outref",
|
||||
"uuid",
|
||||
"vsimd",
|
||||
]
|
||||
|
||||
|
||||
@@ -505,7 +505,7 @@ ec4rs = "1.1"
|
||||
emojis = "0.6.1"
|
||||
env_logger = "0.11"
|
||||
exec = "0.3.1"
|
||||
fancy-regex = "0.16.0"
|
||||
fancy-regex = "0.14.0"
|
||||
fork = "0.4.0"
|
||||
futures = "0.3"
|
||||
futures-batch = "0.6.1"
|
||||
@@ -531,7 +531,7 @@ indoc = "2"
|
||||
inventory = "0.3.19"
|
||||
itertools = "0.14.0"
|
||||
json_dotpath = "1.1"
|
||||
jsonschema = "0.37.0"
|
||||
jsonschema = "0.30.0"
|
||||
jsonwebtoken = "9.3"
|
||||
jupyter-protocol = "0.10.0"
|
||||
jupyter-websocket-client = "0.15.0"
|
||||
@@ -658,7 +658,7 @@ sysinfo = "0.37.0"
|
||||
take-until = "0.2.0"
|
||||
tempfile = "3.20.0"
|
||||
thiserror = "2.0.12"
|
||||
tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "2570c4387a8505fb8f1d3f3557454b474f1e8271" }
|
||||
tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "7249f999c5fdf9bf3cc5c288c964454e4dac0c00" }
|
||||
time = { version = "0.3", features = [
|
||||
"macros",
|
||||
"parsing",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1896_18)">
|
||||
<path d="M11.094 3.09999H8.952L12.858 12.9H15L11.094 3.09999Z" fill="black"/>
|
||||
<path d="M4.906 3.09999L1 12.9H3.184L3.98284 10.842H8.06915L8.868 12.9H11.052L7.146 3.09999H4.906ZM4.68928 9.02199L6.026 5.57799L7.3627 9.02199H4.68928Z" fill="black"/>
|
||||
<path d="M11.094 3.09999H8.952L12.858 12.9H15L11.094 3.09999Z" fill="#1F1F1E"/>
|
||||
<path d="M4.906 3.09999L1 12.9H3.184L3.98284 10.842H8.06915L8.868 12.9H11.052L7.146 3.09999H4.906ZM4.68928 9.02199L6.026 5.57799L7.3627 9.02199H4.68928Z" fill="#1F1F1E"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1896_18">
|
||||
|
Before Width: | Height: | Size: 526 B After Width: | Height: | Size: 530 B |
@@ -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,132 +1903,135 @@
|
||||
"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.
|
||||
@@ -2037,7 +2040,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
|
||||
@@ -2055,11 +2058,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": {
|
||||
@@ -2080,19 +2083,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"
|
||||
@@ -2106,7 +2109,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": {
|
||||
@@ -2120,7 +2123,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.
|
||||
@@ -2153,9 +2156,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
|
||||
//
|
||||
@@ -2214,7 +2217,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
|
||||
@@ -2241,5 +2244,5 @@
|
||||
// Useful for filtering out noisy logs or enabling more verbose logging.
|
||||
//
|
||||
// Example: {"log": {"client": "warn"}}
|
||||
"log": {},
|
||||
"log": {}
|
||||
}
|
||||
|
||||
@@ -204,21 +204,12 @@ pub trait AgentModelSelector: 'static {
|
||||
}
|
||||
}
|
||||
|
||||
/// Icon for a model in the model selector.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AgentModelIcon {
|
||||
/// A built-in icon from Zed's icon set.
|
||||
Named(IconName),
|
||||
/// Path to a custom SVG icon file.
|
||||
Path(SharedString),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AgentModelInfo {
|
||||
pub id: acp::ModelId,
|
||||
pub name: SharedString,
|
||||
pub description: Option<SharedString>,
|
||||
pub icon: Option<AgentModelIcon>,
|
||||
pub icon: Option<IconName>,
|
||||
}
|
||||
|
||||
impl From<acp::ModelInfo> for AgentModelInfo {
|
||||
|
||||
@@ -18,7 +18,7 @@ pub use templates::*;
|
||||
pub use thread::*;
|
||||
pub use tools::*;
|
||||
|
||||
use acp_thread::{AcpThread, AgentModelIcon, AgentModelSelector};
|
||||
use acp_thread::{AcpThread, AgentModelSelector};
|
||||
use agent_client_protocol as acp;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -161,16 +161,11 @@ impl LanguageModels {
|
||||
model: &Arc<dyn LanguageModel>,
|
||||
provider: &Arc<dyn LanguageModelProvider>,
|
||||
) -> acp_thread::AgentModelInfo {
|
||||
let icon = if let Some(path) = provider.icon_path() {
|
||||
Some(AgentModelIcon::Path(path))
|
||||
} else {
|
||||
Some(AgentModelIcon::Named(provider.icon()))
|
||||
};
|
||||
acp_thread::AgentModelInfo {
|
||||
id: Self::model_id(model),
|
||||
name: model.name().0,
|
||||
description: None,
|
||||
icon,
|
||||
icon: Some(provider.icon()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1361,7 +1356,7 @@ mod internal_tests {
|
||||
id: acp::ModelId::new("fake/fake"),
|
||||
name: "Fake".into(),
|
||||
description: None,
|
||||
icon: Some(AgentModelIcon::Named(ui::IconName::ZedAssistant)),
|
||||
icon: Some(ui::IconName::ZedAssistant),
|
||||
}]
|
||||
)])
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{cmp::Reverse, rc::Rc, sync::Arc};
|
||||
|
||||
use acp_thread::{AgentModelIcon, AgentModelInfo, AgentModelList, AgentModelSelector};
|
||||
use acp_thread::{AgentModelInfo, AgentModelList, AgentModelSelector};
|
||||
use agent_servers::AgentServer;
|
||||
use anyhow::Result;
|
||||
use collections::IndexMap;
|
||||
@@ -292,18 +292,12 @@ impl PickerDelegate for AcpModelPickerDelegate {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_1p5()
|
||||
.map(|this| match &model_info.icon {
|
||||
Some(AgentModelIcon::Path(path)) => this.child(
|
||||
Icon::from_path(path.clone())
|
||||
.when_some(model_info.icon, |this, icon| {
|
||||
this.child(
|
||||
Icon::new(icon)
|
||||
.color(model_icon_color)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
Some(AgentModelIcon::Named(icon)) => this.child(
|
||||
Icon::new(*icon)
|
||||
.color(model_icon_color)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
None => this,
|
||||
.size(IconSize::Small)
|
||||
)
|
||||
})
|
||||
.child(Label::new(model_info.name.clone()).truncate()),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use acp_thread::{AgentModelIcon, AgentModelInfo, AgentModelSelector};
|
||||
use acp_thread::{AgentModelInfo, AgentModelSelector};
|
||||
use agent_servers::AgentServer;
|
||||
use fs::Fs;
|
||||
use gpui::{Entity, FocusHandle};
|
||||
@@ -64,7 +64,7 @@ impl Render for AcpModelSelectorPopover {
|
||||
.map(|model| model.name.clone())
|
||||
.unwrap_or_else(|| SharedString::from("Select a Model"));
|
||||
|
||||
let model_icon = model.as_ref().and_then(|model| model.icon.clone());
|
||||
let model_icon = model.as_ref().and_then(|model| model.icon);
|
||||
|
||||
let focus_handle = self.focus_handle.clone();
|
||||
|
||||
@@ -78,13 +78,8 @@ impl Render for AcpModelSelectorPopover {
|
||||
self.selector.clone(),
|
||||
ButtonLike::new("active-model")
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.when_some(model_icon, |this, icon| match icon {
|
||||
AgentModelIcon::Path(path) => {
|
||||
this.child(Icon::from_path(path).color(color).size(IconSize::XSmall))
|
||||
}
|
||||
AgentModelIcon::Named(icon_name) => {
|
||||
this.child(Icon::new(icon_name).color(color).size(IconSize::XSmall))
|
||||
}
|
||||
.when_some(model_icon, |this, icon| {
|
||||
this.child(Icon::new(icon).color(color).size(IconSize::XSmall))
|
||||
})
|
||||
.child(
|
||||
Label::new(model_name)
|
||||
|
||||
@@ -260,15 +260,11 @@ impl AgentConfiguration {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_1p5()
|
||||
.child(if let Some(icon_path) = provider.icon_path() {
|
||||
Icon::from_external_svg(icon_path)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted)
|
||||
} else {
|
||||
.child(
|
||||
Icon::new(provider.icon())
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted)
|
||||
})
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
||||
@@ -73,8 +73,7 @@ impl Render for AgentModelSelector {
|
||||
.map(|model| model.model.name().0)
|
||||
.unwrap_or_else(|| SharedString::from("Select a Model"));
|
||||
|
||||
let provider_icon_path = model.as_ref().and_then(|model| model.provider.icon_path());
|
||||
let provider_icon_name = model.as_ref().map(|model| model.provider.icon());
|
||||
let provider_icon = model.as_ref().map(|model| model.provider.icon());
|
||||
let color = if self.menu_handle.is_deployed() {
|
||||
Color::Accent
|
||||
} else {
|
||||
@@ -86,17 +85,8 @@ impl Render for AgentModelSelector {
|
||||
PickerPopoverMenu::new(
|
||||
self.selector.clone(),
|
||||
ButtonLike::new("active-model")
|
||||
.when_some(provider_icon_path.clone(), |this, icon_path| {
|
||||
this.child(
|
||||
Icon::from_external_svg(icon_path)
|
||||
.color(color)
|
||||
.size(IconSize::XSmall),
|
||||
)
|
||||
})
|
||||
.when(provider_icon_path.is_none(), |this| {
|
||||
this.when_some(provider_icon_name, |this, icon| {
|
||||
this.child(Icon::new(icon).color(color).size(IconSize::XSmall))
|
||||
})
|
||||
.when_some(provider_icon, |this, icon| {
|
||||
this.child(Icon::new(icon).color(color).size(IconSize::XSmall))
|
||||
})
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.child(
|
||||
|
||||
@@ -346,13 +346,9 @@ fn init_language_model_settings(cx: &mut App) {
|
||||
cx.subscribe(
|
||||
&LanguageModelRegistry::global(cx),
|
||||
|_, event: &language_model::Event, cx| match event {
|
||||
language_model::Event::ProviderStateChanged(_) => {
|
||||
update_active_language_model_from_settings(cx);
|
||||
}
|
||||
language_model::Event::AddedProvider(_) => {
|
||||
update_active_language_model_from_settings(cx);
|
||||
}
|
||||
language_model::Event::RemovedProvider(_) => {
|
||||
language_model::Event::ProviderStateChanged(_)
|
||||
| language_model::Event::AddedProvider(_)
|
||||
| language_model::Event::RemovedProvider(_) => {
|
||||
update_active_language_model_from_settings(cx);
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -60,8 +60,7 @@ impl<T: 'static> EventEmitter<PromptEditorEvent> for PromptEditor<T> {}
|
||||
|
||||
impl<T: 'static> Render for PromptEditor<T> {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
|
||||
let mut buttons = Vec::new();
|
||||
let mut action_buttons = Vec::new();
|
||||
|
||||
const RIGHT_PADDING: Pixels = px(9.);
|
||||
|
||||
@@ -74,7 +73,7 @@ impl<T: 'static> Render for PromptEditor<T> {
|
||||
let codegen = codegen.read(cx);
|
||||
|
||||
if codegen.alternative_count(cx) > 1 {
|
||||
buttons.push(self.render_cycle_controls(codegen, cx));
|
||||
action_buttons.push(self.render_cycle_controls(codegen, cx));
|
||||
}
|
||||
|
||||
let editor_margins = editor_margins.lock();
|
||||
@@ -85,10 +84,7 @@ impl<T: 'static> Render for PromptEditor<T> {
|
||||
|
||||
(left_gutter_width, right_padding)
|
||||
}
|
||||
PromptEditorMode::Terminal { .. } => {
|
||||
// Give the equivalent of the same left-padding that we're using on the right
|
||||
(Pixels::from(40.0), Pixels::from(24.))
|
||||
}
|
||||
PromptEditorMode::Terminal { .. } => (Pixels::from(40.0), Pixels::from(24.)),
|
||||
};
|
||||
|
||||
let bottom_padding = match &self.mode {
|
||||
@@ -96,7 +92,7 @@ impl<T: 'static> Render for PromptEditor<T> {
|
||||
PromptEditorMode::Terminal { .. } => rems_from_px(8.0),
|
||||
};
|
||||
|
||||
buttons.extend(self.render_buttons(window, cx));
|
||||
action_buttons.extend(self.render_buttons(window, cx));
|
||||
|
||||
let menu_visible = self.is_completions_menu_visible(cx);
|
||||
let add_context_button = IconButton::new("add-context", IconName::AtSign)
|
||||
@@ -109,93 +105,48 @@ impl<T: 'static> Render for PromptEditor<T> {
|
||||
})
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
this.trigger_completion_menu(window, cx);
|
||||
}));
|
||||
}))
|
||||
.into_any_element();
|
||||
|
||||
let close_button = self.render_close_button(cx);
|
||||
|
||||
let error_message = if let CodegenStatus::Error(error) = self.codegen_status(cx) {
|
||||
Some(SharedString::from(error.to_string()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let editor = self.render_editor(window, cx);
|
||||
let model_selector = self.model_selector.clone().into_any_element();
|
||||
|
||||
v_flex()
|
||||
.key_context("PromptEditor")
|
||||
.capture_action(cx.listener(Self::paste))
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.block_mouse_except_scroll()
|
||||
.gap_0p5()
|
||||
.border_y_1()
|
||||
.border_color(cx.theme().status().info_border)
|
||||
.size_full()
|
||||
.pt_0p5()
|
||||
.pb(bottom_padding)
|
||||
.pr(right_padding)
|
||||
.cursor(CursorStyle::Arrow)
|
||||
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
|
||||
this.model_selector
|
||||
.update(cx, |model_selector, cx| model_selector.toggle(window, cx));
|
||||
}))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(Self::move_up))
|
||||
.on_action(cx.listener(Self::move_down))
|
||||
.capture_action(cx.listener(Self::cycle_prev))
|
||||
.capture_action(cx.listener(Self::cycle_next))
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
.cursor(CursorStyle::Arrow)
|
||||
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
|
||||
this.model_selector
|
||||
.update(cx, |model_selector, cx| model_selector.toggle(window, cx));
|
||||
}))
|
||||
.on_action(cx.listener(Self::confirm))
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(Self::move_up))
|
||||
.on_action(cx.listener(Self::move_down))
|
||||
.capture_action(cx.listener(Self::cycle_prev))
|
||||
.capture_action(cx.listener(Self::cycle_next))
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.flex_shrink_0()
|
||||
.items_center()
|
||||
.h_full()
|
||||
.w(left_gutter_width)
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.child(self.render_close_button(cx))
|
||||
.map(|el| {
|
||||
let CodegenStatus::Error(error) = self.codegen_status(cx) else {
|
||||
return el;
|
||||
};
|
||||
|
||||
let error_message = SharedString::from(error.to_string());
|
||||
el.child(
|
||||
div()
|
||||
.id("error")
|
||||
.tooltip(Tooltip::text(error_message))
|
||||
.child(
|
||||
Icon::new(IconName::XCircle)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Error),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(div().flex_1().child(self.render_editor(window, cx)))
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.children(buttons),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.child(h_flex().flex_shrink_0().w(left_gutter_width))
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.pl_1()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.child(add_context_button)
|
||||
.child(self.model_selector.clone()),
|
||||
),
|
||||
PromptEditorLayout::new(
|
||||
editor,
|
||||
close_button,
|
||||
action_buttons,
|
||||
add_context_button,
|
||||
model_selector,
|
||||
)
|
||||
.error_message(error_message)
|
||||
.left_gutter_width(left_gutter_width)
|
||||
.right_padding(right_padding)
|
||||
.bottom_padding(bottom_padding),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1174,6 +1125,237 @@ impl GenerationMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// A stateless layout component for the inline prompt editor.
|
||||
///
|
||||
/// This component handles the visual layout of the prompt editor UI without
|
||||
/// any behavior. It's used by both `PromptEditor` (with interactive elements)
|
||||
/// and the component preview (with static elements).
|
||||
#[derive(IntoElement)]
|
||||
pub struct PromptEditorLayout {
|
||||
/// The editor element to display
|
||||
editor: AnyElement,
|
||||
/// Close button element (left gutter)
|
||||
close_button: AnyElement,
|
||||
/// Optional error message to display (left gutter, shown as error icon when present)
|
||||
error_message: Option<SharedString>,
|
||||
/// Action buttons (right side: start/stop/accept/restart + cycle controls)
|
||||
action_buttons: Vec<AnyElement>,
|
||||
/// Add context button (bottom left, @ button)
|
||||
add_context_button: AnyElement,
|
||||
/// Model selector element (bottom right)
|
||||
model_selector: AnyElement,
|
||||
/// Left gutter width for alignment
|
||||
left_gutter_width: Pixels,
|
||||
/// Right padding
|
||||
right_padding: Pixels,
|
||||
/// Bottom padding
|
||||
bottom_padding: Rems,
|
||||
}
|
||||
|
||||
impl PromptEditorLayout {
|
||||
pub fn new(
|
||||
editor: AnyElement,
|
||||
close_button: AnyElement,
|
||||
action_buttons: Vec<AnyElement>,
|
||||
add_context_button: AnyElement,
|
||||
model_selector: AnyElement,
|
||||
) -> Self {
|
||||
Self {
|
||||
editor,
|
||||
close_button,
|
||||
error_message: None,
|
||||
action_buttons,
|
||||
add_context_button,
|
||||
model_selector,
|
||||
left_gutter_width: px(40.0),
|
||||
right_padding: px(9.0),
|
||||
bottom_padding: rems_from_px(2.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_message(mut self, error_message: impl Into<Option<SharedString>>) -> Self {
|
||||
self.error_message = error_message.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_gutter_width(mut self, width: Pixels) -> Self {
|
||||
self.left_gutter_width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn right_padding(mut self, padding: Pixels) -> Self {
|
||||
self.right_padding = padding;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bottom_padding(mut self, padding: Rems) -> Self {
|
||||
self.bottom_padding = padding;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a PromptEditorLayout for preview/static rendering.
|
||||
///
|
||||
/// This constructor handles creating all the static (non-interactive) buttons
|
||||
/// based on the codegen status, mode, and other parameters. It's used by the
|
||||
/// component preview system.
|
||||
pub fn preview(
|
||||
editor: AnyElement,
|
||||
codegen_status: CodegenStatus,
|
||||
mode: GenerationMode,
|
||||
edited_since_done: bool,
|
||||
cx: &App,
|
||||
) -> Self {
|
||||
// Create action buttons based on status
|
||||
let action_buttons = match codegen_status {
|
||||
CodegenStatus::Idle => {
|
||||
vec![
|
||||
Button::new("start", mode.start_label())
|
||||
.label_size(LabelSize::Small)
|
||||
.icon(IconName::Return)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.into_any_element(),
|
||||
]
|
||||
}
|
||||
CodegenStatus::Pending => vec![
|
||||
IconButton::new("stop", IconName::Stop)
|
||||
.icon_color(Color::Error)
|
||||
.shape(IconButtonShape::Square)
|
||||
.into_any_element(),
|
||||
],
|
||||
CodegenStatus::Done => {
|
||||
if edited_since_done {
|
||||
vec![
|
||||
IconButton::new("restart", IconName::RotateCw)
|
||||
.icon_color(Color::Info)
|
||||
.shape(IconButtonShape::Square)
|
||||
.into_any_element(),
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
IconButton::new("accept", IconName::Check)
|
||||
.icon_color(Color::Info)
|
||||
.shape(IconButtonShape::Square)
|
||||
.into_any_element(),
|
||||
]
|
||||
}
|
||||
}
|
||||
CodegenStatus::Error(_) => {
|
||||
vec![
|
||||
IconButton::new("restart", IconName::RotateCw)
|
||||
.icon_color(Color::Info)
|
||||
.shape(IconButtonShape::Square)
|
||||
.into_any_element(),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let close_button = IconButton::new("cancel", IconName::Close)
|
||||
.icon_color(Color::Muted)
|
||||
.shape(IconButtonShape::Square)
|
||||
.into_any_element();
|
||||
|
||||
let add_context_button = IconButton::new("add-context", IconName::AtSign)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.into_any_element();
|
||||
|
||||
let model_selector = div()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.child("Model Selector")
|
||||
.into_any_element();
|
||||
|
||||
let error_message = if let CodegenStatus::Error(error) = codegen_status {
|
||||
Some(SharedString::from(error.to_string()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::new(
|
||||
editor,
|
||||
close_button,
|
||||
action_buttons,
|
||||
add_context_button,
|
||||
model_selector,
|
||||
)
|
||||
.error_message(error_message)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for PromptEditorLayout {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
|
||||
|
||||
v_flex()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.gap_0p5()
|
||||
.border_y_1()
|
||||
.border_color(cx.theme().status().info_border)
|
||||
.size_full()
|
||||
.pt_0p5()
|
||||
.pb(self.bottom_padding)
|
||||
.pr(self.right_padding)
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.flex_shrink_0()
|
||||
.items_center()
|
||||
.h_full()
|
||||
.w(self.left_gutter_width)
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.child(self.close_button)
|
||||
.when_some(self.error_message, |el, error_message| {
|
||||
el.child(
|
||||
div()
|
||||
.id("error")
|
||||
.tooltip(Tooltip::text(error_message))
|
||||
.child(
|
||||
Icon::new(IconName::XCircle)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Error),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(div().flex_1().child(self.editor))
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.children(self.action_buttons),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
WithRemSize::new(ui_font_size)
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.child(h_flex().flex_shrink_0().w(self.left_gutter_width))
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.pl_1()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.child(self.add_context_button)
|
||||
.child(self.model_selector),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stored information that can be used to resurrect a context crease when creating an editor for a past message.
|
||||
#[derive(Clone, Debug)]
|
||||
struct MessageCrease {
|
||||
@@ -1229,3 +1411,144 @@ fn insert_message_creases(
|
||||
editor.fold_creases(creases, false, window, cx);
|
||||
ids
|
||||
}
|
||||
|
||||
mod preview {
|
||||
use component::{Component, ComponentScope, example_group_with_title, single_example};
|
||||
use editor::Editor;
|
||||
use gpui::{AnyElement, App, Window};
|
||||
use ui::prelude::*;
|
||||
|
||||
use super::{CodegenStatus, GenerationMode, PromptEditorLayout};
|
||||
|
||||
// View this component preview using `workspace: open component-preview`
|
||||
#[derive(IntoElement, RegisterComponent)]
|
||||
struct PromptEditorPreview;
|
||||
|
||||
impl Component for PromptEditorPreview {
|
||||
fn scope() -> ComponentScope {
|
||||
ComponentScope::Agent
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
"Inline Prompt Editor"
|
||||
}
|
||||
|
||||
fn sort_name() -> &'static str {
|
||||
"AgentInlinePromptEditor"
|
||||
}
|
||||
|
||||
fn preview(window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
let editor = window.use_state(cx, |window, cx| {
|
||||
let mut editor = Editor::single_line(window, cx);
|
||||
editor.set_placeholder_text("How can I help?", window, cx);
|
||||
editor
|
||||
});
|
||||
|
||||
Some(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(example_group_with_title(
|
||||
"Idle State",
|
||||
vec![
|
||||
single_example(
|
||||
"Generate",
|
||||
div()
|
||||
.w(px(600.))
|
||||
.child(PromptEditorLayout::preview(
|
||||
editor.clone().into_any_element(),
|
||||
CodegenStatus::Idle,
|
||||
GenerationMode::Generate,
|
||||
false,
|
||||
cx,
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Transform",
|
||||
div()
|
||||
.w(px(600.))
|
||||
.child(PromptEditorLayout::preview(
|
||||
editor.clone().into_any_element(),
|
||||
CodegenStatus::Idle,
|
||||
GenerationMode::Transform,
|
||||
false,
|
||||
cx,
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
))
|
||||
.child(example_group_with_title(
|
||||
"Pending State",
|
||||
vec![single_example(
|
||||
"Stop Button",
|
||||
div()
|
||||
.w(px(600.))
|
||||
.child(PromptEditorLayout::preview(
|
||||
editor.clone().into_any_element(),
|
||||
CodegenStatus::Pending,
|
||||
GenerationMode::Generate,
|
||||
false,
|
||||
cx,
|
||||
))
|
||||
.into_any_element(),
|
||||
)],
|
||||
))
|
||||
.child(example_group_with_title(
|
||||
"Done State",
|
||||
vec![
|
||||
single_example(
|
||||
"Accept Button",
|
||||
div()
|
||||
.w(px(600.))
|
||||
.child(PromptEditorLayout::preview(
|
||||
editor.clone().into_any_element(),
|
||||
CodegenStatus::Done,
|
||||
GenerationMode::Generate,
|
||||
false,
|
||||
cx,
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Edited Since Done (Restart)",
|
||||
div()
|
||||
.w(px(600.))
|
||||
.child(PromptEditorLayout::preview(
|
||||
editor.clone().into_any_element(),
|
||||
CodegenStatus::Done,
|
||||
GenerationMode::Generate,
|
||||
true,
|
||||
cx,
|
||||
))
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
))
|
||||
.child(example_group_with_title(
|
||||
"Error State",
|
||||
vec![single_example(
|
||||
"Error Indicator with Restart",
|
||||
div()
|
||||
.w(px(600.))
|
||||
.child(PromptEditorLayout::preview(
|
||||
editor.into_any_element(),
|
||||
CodegenStatus::Error(anyhow::anyhow!("Example error message")),
|
||||
GenerationMode::Generate,
|
||||
false,
|
||||
cx,
|
||||
))
|
||||
.into_any_element(),
|
||||
)],
|
||||
))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for PromptEditorPreview {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
div().child("Inline Prompt Editor Preview")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::{cmp::Reverse, sync::Arc};
|
||||
|
||||
use collections::IndexMap;
|
||||
use futures::{StreamExt, channel::mpsc};
|
||||
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
|
||||
use gpui::{Action, AnyElement, App, BackgroundExecutor, DismissEvent, FocusHandle, Task};
|
||||
use gpui::{
|
||||
Action, AnyElement, App, BackgroundExecutor, DismissEvent, FocusHandle, Subscription, Task,
|
||||
};
|
||||
use language_model::{
|
||||
AuthenticateError, ConfiguredModel, LanguageModel, LanguageModelProvider,
|
||||
LanguageModelProviderId, LanguageModelRegistry,
|
||||
AuthenticateError, ConfiguredModel, LanguageModel, LanguageModelProviderId,
|
||||
LanguageModelRegistry,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
@@ -56,12 +57,12 @@ fn all_models(cx: &App) -> GroupedModels {
|
||||
.into_iter()
|
||||
.map(|model| ModelInfo {
|
||||
model,
|
||||
icon: ProviderIcon::from_provider(provider.as_ref()),
|
||||
icon: provider.icon(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let all: Vec<ModelInfo> = providers
|
||||
let all = providers
|
||||
.iter()
|
||||
.flat_map(|provider| {
|
||||
provider
|
||||
@@ -69,7 +70,7 @@ fn all_models(cx: &App) -> GroupedModels {
|
||||
.into_iter()
|
||||
.map(|model| ModelInfo {
|
||||
model,
|
||||
icon: ProviderIcon::from_provider(provider.as_ref()),
|
||||
icon: provider.icon(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
@@ -77,26 +78,10 @@ fn all_models(cx: &App) -> GroupedModels {
|
||||
GroupedModels::new(all, recommended)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ProviderIcon {
|
||||
Name(IconName),
|
||||
Path(SharedString),
|
||||
}
|
||||
|
||||
impl ProviderIcon {
|
||||
fn from_provider(provider: &dyn LanguageModelProvider) -> Self {
|
||||
if let Some(path) = provider.icon_path() {
|
||||
Self::Path(path)
|
||||
} else {
|
||||
Self::Name(provider.icon())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ModelInfo {
|
||||
model: Arc<dyn LanguageModel>,
|
||||
icon: ProviderIcon,
|
||||
icon: IconName,
|
||||
}
|
||||
|
||||
pub struct LanguageModelPickerDelegate {
|
||||
@@ -106,7 +91,7 @@ pub struct LanguageModelPickerDelegate {
|
||||
filtered_entries: Vec<LanguageModelPickerEntry>,
|
||||
selected_index: usize,
|
||||
_authenticate_all_providers_task: Task<()>,
|
||||
_refresh_models_task: Task<()>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
popover_styles: bool,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
@@ -131,40 +116,24 @@ impl LanguageModelPickerDelegate {
|
||||
filtered_entries: entries,
|
||||
get_active_model: Arc::new(get_active_model),
|
||||
_authenticate_all_providers_task: Self::authenticate_all_providers(cx),
|
||||
_refresh_models_task: {
|
||||
// Create a channel to signal when models need refreshing
|
||||
let (refresh_tx, mut refresh_rx) = mpsc::unbounded::<()>();
|
||||
|
||||
// Subscribe to registry events and send refresh signals through the channel
|
||||
let registry = LanguageModelRegistry::global(cx);
|
||||
cx.subscribe(®istry, move |_picker, _, event, _cx| match event {
|
||||
language_model::Event::ProviderStateChanged(_) => {
|
||||
refresh_tx.unbounded_send(()).ok();
|
||||
}
|
||||
language_model::Event::AddedProvider(_) => {
|
||||
refresh_tx.unbounded_send(()).ok();
|
||||
}
|
||||
language_model::Event::RemovedProvider(_) => {
|
||||
refresh_tx.unbounded_send(()).ok();
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Spawn a task that listens for refresh signals and updates the picker
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
while let Some(()) = refresh_rx.next().await {
|
||||
let result = this.update_in(cx, |picker, window, cx| {
|
||||
_subscriptions: vec![cx.subscribe_in(
|
||||
&LanguageModelRegistry::global(cx),
|
||||
window,
|
||||
|picker, _, event, window, cx| {
|
||||
match event {
|
||||
language_model::Event::ProviderStateChanged(_)
|
||||
| language_model::Event::AddedProvider(_)
|
||||
| language_model::Event::RemovedProvider(_) => {
|
||||
let query = picker.query(cx);
|
||||
picker.delegate.all_models = Arc::new(all_models(cx));
|
||||
picker.refresh(window, cx);
|
||||
});
|
||||
if result.is_err() {
|
||||
// Picker was dropped, exit the loop
|
||||
break;
|
||||
// Update matches will automatically drop the previous task
|
||||
// if we get a provider event again
|
||||
picker.update_matches(query, window, cx)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
)],
|
||||
popover_styles,
|
||||
focus_handle,
|
||||
}
|
||||
@@ -535,16 +504,11 @@ impl PickerDelegate for LanguageModelPickerDelegate {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_1p5()
|
||||
.child(match &model_info.icon {
|
||||
ProviderIcon::Name(icon_name) => Icon::new(*icon_name)
|
||||
.child(
|
||||
Icon::new(model_info.icon)
|
||||
.color(model_icon_color)
|
||||
.size(IconSize::Small),
|
||||
ProviderIcon::Path(icon_path) => {
|
||||
Icon::from_external_svg(icon_path.clone())
|
||||
.color(model_icon_color)
|
||||
.size(IconSize::Small)
|
||||
}
|
||||
})
|
||||
)
|
||||
.child(Label::new(model_info.model.name().0).truncate()),
|
||||
)
|
||||
.end_slot(div().pr_3().when(is_selected, |this| {
|
||||
@@ -693,7 +657,7 @@ mod tests {
|
||||
.into_iter()
|
||||
.map(|(provider, name)| ModelInfo {
|
||||
model: Arc::new(TestLanguageModel::new(name, provider)),
|
||||
icon: ProviderIcon::Name(IconName::Ai),
|
||||
icon: IconName::Ai,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -2097,8 +2097,7 @@ impl TextThreadEditor {
|
||||
.default_model()
|
||||
.map(|default| default.provider);
|
||||
|
||||
let provider_icon_path = active_provider.as_ref().and_then(|p| p.icon_path());
|
||||
let provider_icon_name = match &active_provider {
|
||||
let provider_icon = match active_provider {
|
||||
Some(provider) => provider.icon(),
|
||||
None => IconName::Ai,
|
||||
};
|
||||
@@ -2110,16 +2109,6 @@ impl TextThreadEditor {
|
||||
(Color::Muted, IconName::ChevronDown)
|
||||
};
|
||||
|
||||
let provider_icon_element = if let Some(icon_path) = provider_icon_path {
|
||||
Icon::from_external_svg(icon_path)
|
||||
.color(color)
|
||||
.size(IconSize::XSmall)
|
||||
} else {
|
||||
Icon::new(provider_icon_name)
|
||||
.color(color)
|
||||
.size(IconSize::XSmall)
|
||||
};
|
||||
|
||||
PickerPopoverMenu::new(
|
||||
self.language_model_selector.clone(),
|
||||
ButtonLike::new("active-model")
|
||||
@@ -2127,7 +2116,7 @@ impl TextThreadEditor {
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_0p5()
|
||||
.child(provider_icon_element)
|
||||
.child(Icon::new(provider_icon).color(color).size(IconSize::XSmall))
|
||||
.child(
|
||||
Label::new(model_name)
|
||||
.color(color)
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
use gpui::{Action, IntoElement, ParentElement, RenderOnce, point};
|
||||
use language_model::{LanguageModelProvider, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
|
||||
use language_model::{LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
|
||||
use ui::{Divider, List, ListBulletItem, prelude::*};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ProviderIcon {
|
||||
Name(IconName),
|
||||
Path(SharedString),
|
||||
}
|
||||
|
||||
impl ProviderIcon {
|
||||
fn from_provider(provider: &dyn LanguageModelProvider) -> Self {
|
||||
if let Some(path) = provider.icon_path() {
|
||||
Self::Path(path)
|
||||
} else {
|
||||
Self::Name(provider.icon())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ApiKeysWithProviders {
|
||||
configured_providers: Vec<(ProviderIcon, SharedString)>,
|
||||
configured_providers: Vec<(IconName, SharedString)>,
|
||||
}
|
||||
|
||||
impl ApiKeysWithProviders {
|
||||
@@ -42,19 +26,14 @@ impl ApiKeysWithProviders {
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_configured_providers(cx: &App) -> Vec<(ProviderIcon, SharedString)> {
|
||||
fn compute_configured_providers(cx: &App) -> Vec<(IconName, SharedString)> {
|
||||
LanguageModelRegistry::read_global(cx)
|
||||
.providers()
|
||||
.iter()
|
||||
.filter(|provider| {
|
||||
provider.is_authenticated(cx) && provider.id() != ZED_CLOUD_PROVIDER_ID
|
||||
})
|
||||
.map(|provider| {
|
||||
(
|
||||
ProviderIcon::from_provider(provider.as_ref()),
|
||||
provider.name().0,
|
||||
)
|
||||
})
|
||||
.map(|provider| (provider.icon(), provider.name().0))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -68,14 +47,7 @@ impl Render for ApiKeysWithProviders {
|
||||
.map(|(icon, name)| {
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.child(match icon {
|
||||
ProviderIcon::Name(icon_name) => Icon::new(icon_name)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Muted),
|
||||
ProviderIcon::Path(icon_path) => Icon::from_external_svg(icon_path)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Muted),
|
||||
})
|
||||
.child(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted))
|
||||
.child(Label::new(name))
|
||||
});
|
||||
div()
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{AgentPanelOnboardingCard, ApiKeysWithoutProviders, ZedAiOnboarding};
|
||||
pub struct AgentPanelOnboarding {
|
||||
user_store: Entity<UserStore>,
|
||||
client: Arc<Client>,
|
||||
has_configured_providers: bool,
|
||||
configured_providers: Vec<(IconName, SharedString)>,
|
||||
continue_with_zed_ai: Arc<dyn Fn(&mut Window, &mut App)>,
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ impl AgentPanelOnboarding {
|
||||
language_model::Event::ProviderStateChanged(_)
|
||||
| language_model::Event::AddedProvider(_)
|
||||
| language_model::Event::RemovedProvider(_) => {
|
||||
this.has_configured_providers = Self::has_configured_providers(cx)
|
||||
this.configured_providers = Self::compute_available_providers(cx)
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
@@ -38,16 +38,20 @@ impl AgentPanelOnboarding {
|
||||
Self {
|
||||
user_store,
|
||||
client,
|
||||
has_configured_providers: Self::has_configured_providers(cx),
|
||||
configured_providers: Self::compute_available_providers(cx),
|
||||
continue_with_zed_ai: Arc::new(continue_with_zed_ai),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_configured_providers(cx: &App) -> bool {
|
||||
fn compute_available_providers(cx: &App) -> Vec<(IconName, SharedString)> {
|
||||
LanguageModelRegistry::read_global(cx)
|
||||
.providers()
|
||||
.iter()
|
||||
.any(|provider| provider.is_authenticated(cx) && provider.id() != ZED_CLOUD_PROVIDER_ID)
|
||||
.filter(|provider| {
|
||||
provider.is_authenticated(cx) && provider.id() != ZED_CLOUD_PROVIDER_ID
|
||||
})
|
||||
.map(|provider| (provider.icon(), provider.name().0))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +81,7 @@ impl Render for AgentPanelOnboarding {
|
||||
}),
|
||||
)
|
||||
.map(|this| {
|
||||
if enrolled_in_trial || is_pro_user || self.has_configured_providers {
|
||||
if enrolled_in_trial || is_pro_user || !self.configured_providers.is_empty() {
|
||||
this
|
||||
} else {
|
||||
this.child(ApiKeysWithoutProviders::new())
|
||||
|
||||
@@ -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::ModelMode;
|
||||
pub use settings::{AnthropicAvailableModel as AvailableModel, ModelMode};
|
||||
use strum::{EnumIter, EnumString};
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
@@ -584,100 +584,41 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cross_region_inference_id(
|
||||
&self,
|
||||
region: &str,
|
||||
allow_global: bool,
|
||||
) -> anyhow::Result<String> {
|
||||
// List derived from here:
|
||||
// https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system
|
||||
let model_id = self.request_id();
|
||||
|
||||
let supports_global = matches!(
|
||||
self,
|
||||
Model::ClaudeOpus4_5
|
||||
| Model::ClaudeOpus4_5Thinking
|
||||
| Model::ClaudeHaiku4_5
|
||||
| Model::ClaudeSonnet4
|
||||
| Model::ClaudeSonnet4Thinking
|
||||
| Model::ClaudeSonnet4_5
|
||||
| Model::ClaudeSonnet4_5Thinking
|
||||
);
|
||||
|
||||
pub fn cross_region_inference_id(&self, region: &str) -> anyhow::Result<String> {
|
||||
let region_group = if region.starts_with("us-gov-") {
|
||||
"us-gov"
|
||||
} else if region.starts_with("us-")
|
||||
|| region.starts_with("ca-")
|
||||
|| region.starts_with("sa-")
|
||||
{
|
||||
if allow_global && supports_global {
|
||||
"global"
|
||||
} else {
|
||||
"us"
|
||||
}
|
||||
} else if region.starts_with("us-") {
|
||||
"us"
|
||||
} else if region.starts_with("eu-") {
|
||||
if allow_global && supports_global {
|
||||
"global"
|
||||
} else {
|
||||
"eu"
|
||||
}
|
||||
"eu"
|
||||
} else if region.starts_with("ap-") || region == "me-central-1" || region == "me-south-1" {
|
||||
if allow_global && supports_global {
|
||||
"global"
|
||||
} else {
|
||||
"apac"
|
||||
}
|
||||
"apac"
|
||||
} else if region.starts_with("ca-") || region.starts_with("sa-") {
|
||||
// Canada and South America regions - default to US profiles
|
||||
"us"
|
||||
} else {
|
||||
anyhow::bail!("Unsupported Region {region}");
|
||||
};
|
||||
|
||||
match (self, region_group, region) {
|
||||
(Model::Custom { .. }, _, _) => Ok(self.request_id().into()),
|
||||
let model_id = self.request_id();
|
||||
|
||||
(
|
||||
Model::ClaudeOpus4_5
|
||||
| Model::ClaudeOpus4_5Thinking
|
||||
| Model::ClaudeHaiku4_5
|
||||
| Model::ClaudeSonnet4
|
||||
| Model::ClaudeSonnet4Thinking
|
||||
| Model::ClaudeSonnet4_5
|
||||
| Model::ClaudeSonnet4_5Thinking,
|
||||
"global",
|
||||
_,
|
||||
) => Ok(format!("{}.{}", region_group, model_id)),
|
||||
match (self, region_group) {
|
||||
// Custom models can't have CRI IDs
|
||||
(Model::Custom { .. }, _) => Ok(self.request_id().into()),
|
||||
|
||||
(
|
||||
Model::Claude3Haiku
|
||||
| Model::Claude3_5Sonnet
|
||||
| Model::Claude3_7Sonnet
|
||||
| Model::Claude3_7SonnetThinking
|
||||
| Model::ClaudeSonnet4_5
|
||||
| Model::ClaudeSonnet4_5Thinking,
|
||||
"us-gov",
|
||||
_,
|
||||
) => Ok(format!("{}.{}", region_group, model_id)),
|
||||
|
||||
(
|
||||
Model::ClaudeHaiku4_5 | Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking,
|
||||
"apac",
|
||||
"ap-southeast-2" | "ap-southeast-4",
|
||||
) => Ok(format!("au.{}", model_id)),
|
||||
|
||||
(
|
||||
Model::ClaudeHaiku4_5 | Model::ClaudeSonnet4_5 | Model::ClaudeSonnet4_5Thinking,
|
||||
"apac",
|
||||
"ap-northeast-1" | "ap-northeast-3",
|
||||
) => Ok(format!("jp.{}", model_id)),
|
||||
|
||||
(Model::AmazonNovaLite, "us", r) if r.starts_with("ca-") => {
|
||||
Ok(format!("ca.{}", model_id))
|
||||
// Models with US Gov only
|
||||
(Model::Claude3_5Sonnet, "us-gov") | (Model::Claude3Haiku, "us-gov") => {
|
||||
Ok(format!("{}.{}", region_group, model_id))
|
||||
}
|
||||
|
||||
// Available everywhere
|
||||
(Model::AmazonNovaLite | Model::AmazonNovaMicro | Model::AmazonNovaPro, _) => {
|
||||
Ok(format!("{}.{}", region_group, model_id))
|
||||
}
|
||||
|
||||
// Models in US
|
||||
(
|
||||
Model::AmazonNovaPremier
|
||||
| Model::AmazonNovaLite
|
||||
| Model::AmazonNovaMicro
|
||||
| Model::AmazonNovaPro
|
||||
| Model::Claude3_5Haiku
|
||||
| Model::ClaudeHaiku4_5
|
||||
| Model::Claude3_5Sonnet
|
||||
@@ -714,18 +655,16 @@ impl Model {
|
||||
| Model::PalmyraWriterX4
|
||||
| Model::PalmyraWriterX5,
|
||||
"us",
|
||||
_,
|
||||
) => Ok(format!("{}.{}", region_group, model_id)),
|
||||
|
||||
// Models available in EU
|
||||
(
|
||||
Model::AmazonNovaLite
|
||||
| Model::AmazonNovaMicro
|
||||
| Model::AmazonNovaPro
|
||||
| Model::Claude3_5Sonnet
|
||||
Model::Claude3_5Sonnet
|
||||
| Model::ClaudeHaiku4_5
|
||||
| Model::Claude3_7Sonnet
|
||||
| Model::Claude3_7SonnetThinking
|
||||
| Model::ClaudeSonnet4
|
||||
| Model::ClaudeSonnet4Thinking
|
||||
| Model::ClaudeSonnet4_5
|
||||
| Model::ClaudeSonnet4_5Thinking
|
||||
| Model::Claude3Haiku
|
||||
@@ -734,26 +673,26 @@ impl Model {
|
||||
| Model::MetaLlama323BInstructV1
|
||||
| Model::MistralPixtralLarge2502V1,
|
||||
"eu",
|
||||
_,
|
||||
) => Ok(format!("{}.{}", region_group, model_id)),
|
||||
|
||||
// Models available in APAC
|
||||
(
|
||||
Model::AmazonNovaLite
|
||||
| Model::AmazonNovaMicro
|
||||
| Model::AmazonNovaPro
|
||||
| Model::Claude3_5Sonnet
|
||||
Model::Claude3_5Sonnet
|
||||
| Model::Claude3_5SonnetV2
|
||||
| Model::ClaudeHaiku4_5
|
||||
| Model::Claude3Haiku
|
||||
| Model::Claude3Sonnet
|
||||
| Model::Claude3_7Sonnet
|
||||
| Model::Claude3_7SonnetThinking
|
||||
| Model::ClaudeSonnet4
|
||||
| Model::Claude3Haiku
|
||||
| Model::Claude3Sonnet,
|
||||
| Model::ClaudeSonnet4Thinking
|
||||
| Model::ClaudeSonnet4_5
|
||||
| Model::ClaudeSonnet4_5Thinking,
|
||||
"apac",
|
||||
_,
|
||||
) => Ok(format!("{}.{}", region_group, model_id)),
|
||||
|
||||
_ => Ok(model_id.into()),
|
||||
// Any other combination is not supported
|
||||
_ => Ok(self.request_id().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -766,15 +705,15 @@ mod tests {
|
||||
fn test_us_region_inference_ids() -> anyhow::Result<()> {
|
||||
// Test US regions
|
||||
assert_eq!(
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("us-east-1", false)?,
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("us-east-1")?,
|
||||
"us.anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("us-west-2", false)?,
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("us-west-2")?,
|
||||
"us.anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::AmazonNovaPro.cross_region_inference_id("us-east-2", false)?,
|
||||
Model::AmazonNovaPro.cross_region_inference_id("us-east-2")?,
|
||||
"us.amazon.nova-pro-v1:0"
|
||||
);
|
||||
Ok(())
|
||||
@@ -784,19 +723,19 @@ mod tests {
|
||||
fn test_eu_region_inference_ids() -> anyhow::Result<()> {
|
||||
// Test European regions
|
||||
assert_eq!(
|
||||
Model::ClaudeSonnet4.cross_region_inference_id("eu-west-1", false)?,
|
||||
Model::ClaudeSonnet4.cross_region_inference_id("eu-west-1")?,
|
||||
"eu.anthropic.claude-sonnet-4-20250514-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1", false)?,
|
||||
Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1")?,
|
||||
"eu.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::Claude3Sonnet.cross_region_inference_id("eu-west-1", false)?,
|
||||
Model::Claude3Sonnet.cross_region_inference_id("eu-west-1")?,
|
||||
"eu.anthropic.claude-3-sonnet-20240229-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::AmazonNovaMicro.cross_region_inference_id("eu-north-1", false)?,
|
||||
Model::AmazonNovaMicro.cross_region_inference_id("eu-north-1")?,
|
||||
"eu.amazon.nova-micro-v1:0"
|
||||
);
|
||||
Ok(())
|
||||
@@ -806,15 +745,15 @@ mod tests {
|
||||
fn test_apac_region_inference_ids() -> anyhow::Result<()> {
|
||||
// Test Asia-Pacific regions
|
||||
assert_eq!(
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-northeast-1", false)?,
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-northeast-1")?,
|
||||
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-southeast-2", false)?,
|
||||
Model::Claude3_5SonnetV2.cross_region_inference_id("ap-southeast-2")?,
|
||||
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::AmazonNovaLite.cross_region_inference_id("ap-south-1", false)?,
|
||||
Model::AmazonNovaLite.cross_region_inference_id("ap-south-1")?,
|
||||
"apac.amazon.nova-lite-v1:0"
|
||||
);
|
||||
Ok(())
|
||||
@@ -824,11 +763,11 @@ mod tests {
|
||||
fn test_gov_region_inference_ids() -> anyhow::Result<()> {
|
||||
// Test Government regions
|
||||
assert_eq!(
|
||||
Model::Claude3_5Sonnet.cross_region_inference_id("us-gov-east-1", false)?,
|
||||
Model::Claude3_5Sonnet.cross_region_inference_id("us-gov-east-1")?,
|
||||
"us-gov.anthropic.claude-3-5-sonnet-20240620-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::Claude3Haiku.cross_region_inference_id("us-gov-west-1", false)?,
|
||||
Model::Claude3Haiku.cross_region_inference_id("us-gov-west-1")?,
|
||||
"us-gov.anthropic.claude-3-haiku-20240307-v1:0"
|
||||
);
|
||||
Ok(())
|
||||
@@ -838,15 +777,15 @@ mod tests {
|
||||
fn test_meta_models_inference_ids() -> anyhow::Result<()> {
|
||||
// Test Meta models
|
||||
assert_eq!(
|
||||
Model::MetaLlama370BInstructV1.cross_region_inference_id("us-east-1", false)?,
|
||||
Model::MetaLlama370BInstructV1.cross_region_inference_id("us-east-1")?,
|
||||
"meta.llama3-70b-instruct-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::MetaLlama3170BInstructV1.cross_region_inference_id("us-east-1", false)?,
|
||||
Model::MetaLlama3170BInstructV1.cross_region_inference_id("us-east-1")?,
|
||||
"us.meta.llama3-1-70b-instruct-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::MetaLlama321BInstructV1.cross_region_inference_id("eu-west-1", false)?,
|
||||
Model::MetaLlama321BInstructV1.cross_region_inference_id("eu-west-1")?,
|
||||
"eu.meta.llama3-2-1b-instruct-v1:0"
|
||||
);
|
||||
Ok(())
|
||||
@@ -857,11 +796,11 @@ mod tests {
|
||||
// Mistral models don't follow the regional prefix pattern,
|
||||
// so they should return their original IDs
|
||||
assert_eq!(
|
||||
Model::MistralMistralLarge2402V1.cross_region_inference_id("us-east-1", false)?,
|
||||
Model::MistralMistralLarge2402V1.cross_region_inference_id("us-east-1")?,
|
||||
"mistral.mistral-large-2402-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::MistralMixtral8x7BInstructV0.cross_region_inference_id("eu-west-1", false)?,
|
||||
Model::MistralMixtral8x7BInstructV0.cross_region_inference_id("eu-west-1")?,
|
||||
"mistral.mixtral-8x7b-instruct-v0:1"
|
||||
);
|
||||
Ok(())
|
||||
@@ -872,11 +811,11 @@ mod tests {
|
||||
// AI21 models don't follow the regional prefix pattern,
|
||||
// so they should return their original IDs
|
||||
assert_eq!(
|
||||
Model::AI21J2UltraV1.cross_region_inference_id("us-east-1", false)?,
|
||||
Model::AI21J2UltraV1.cross_region_inference_id("us-east-1")?,
|
||||
"ai21.j2-ultra-v1"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::AI21JambaInstructV1.cross_region_inference_id("eu-west-1", false)?,
|
||||
Model::AI21JambaInstructV1.cross_region_inference_id("eu-west-1")?,
|
||||
"ai21.jamba-instruct-v1:0"
|
||||
);
|
||||
Ok(())
|
||||
@@ -887,11 +826,11 @@ mod tests {
|
||||
// Cohere models don't follow the regional prefix pattern,
|
||||
// so they should return their original IDs
|
||||
assert_eq!(
|
||||
Model::CohereCommandRV1.cross_region_inference_id("us-east-1", false)?,
|
||||
Model::CohereCommandRV1.cross_region_inference_id("us-east-1")?,
|
||||
"cohere.command-r-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::CohereCommandTextV14_4k.cross_region_inference_id("ap-southeast-1", false)?,
|
||||
Model::CohereCommandTextV14_4k.cross_region_inference_id("ap-southeast-1")?,
|
||||
"cohere.command-text-v14:7:4k"
|
||||
);
|
||||
Ok(())
|
||||
@@ -911,17 +850,10 @@ mod tests {
|
||||
|
||||
// Custom model should return its name unchanged
|
||||
assert_eq!(
|
||||
custom_model.cross_region_inference_id("us-east-1", false)?,
|
||||
custom_model.cross_region_inference_id("us-east-1")?,
|
||||
"custom.my-model-v1:0"
|
||||
);
|
||||
|
||||
// Test that models without global support fall back to regional when allow_global is true
|
||||
assert_eq!(
|
||||
Model::AmazonNovaPro.cross_region_inference_id("us-east-1", true)?,
|
||||
"us.amazon.nova-pro-v1:0",
|
||||
"Nova Pro should fall back to regional profile even when allow_global is true"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -960,28 +892,3 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_global_inference_ids() -> anyhow::Result<()> {
|
||||
// Test global inference for models that support it when allow_global is true
|
||||
assert_eq!(
|
||||
Model::ClaudeSonnet4.cross_region_inference_id("us-east-1", true)?,
|
||||
"global.anthropic.claude-sonnet-4-20250514-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::ClaudeSonnet4_5.cross_region_inference_id("eu-west-1", true)?,
|
||||
"global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
);
|
||||
assert_eq!(
|
||||
Model::ClaudeHaiku4_5.cross_region_inference_id("ap-south-1", true)?,
|
||||
"global.anthropic.claude-haiku-4-5-20251001-v1:0"
|
||||
);
|
||||
|
||||
// Test that regional prefix is used when allow_global is false
|
||||
assert_eq!(
|
||||
Model::ClaudeSonnet4.cross_region_inference_id("us-east-1", false)?,
|
||||
"us.anthropic.claude-sonnet-4-20250514-v1:0"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1723,10 +1723,6 @@ impl ProtoClient for Client {
|
||||
fn is_via_collab(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn has_wsl_interop(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// prefix for the zed:// url scheme
|
||||
|
||||
@@ -469,8 +469,6 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GetBlobContent>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitCreateBranch>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitChangeBranch>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitCreateRemote>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitRemoveRemote>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::CheckForPushedCommits>)
|
||||
.add_message_handler(broadcast_project_message_from_host::<proto::AdvertiseContexts>)
|
||||
.add_message_handler(update_context)
|
||||
|
||||
@@ -182,7 +182,7 @@ use std::{
|
||||
iter::{self, Peekable},
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
|
||||
ops::{Deref, DerefMut, Not, Range, RangeInclusive},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
@@ -191,7 +191,7 @@ use std::{
|
||||
use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
|
||||
use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _};
|
||||
use theme::{
|
||||
AccentColors, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings,
|
||||
ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings,
|
||||
observe_buffer_font_size_adjustment,
|
||||
};
|
||||
use ui::{
|
||||
@@ -1206,17 +1206,11 @@ pub struct Editor {
|
||||
select_next_is_case_sensitive: Option<bool>,
|
||||
pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
|
||||
applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
|
||||
accent_data: Option<AccentData>,
|
||||
accent_overrides: Vec<SharedString>,
|
||||
fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
|
||||
use_base_text_line_numbers: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct AccentData {
|
||||
colors: AccentColors,
|
||||
overrides: Vec<SharedString>,
|
||||
}
|
||||
|
||||
fn debounce_value(debounce_ms: u64) -> Option<Duration> {
|
||||
if debounce_ms > 0 {
|
||||
Some(Duration::from_millis(debounce_ms))
|
||||
@@ -2360,7 +2354,7 @@ impl Editor {
|
||||
lookup_key: None,
|
||||
select_next_is_case_sensitive: None,
|
||||
applicable_language_settings: HashMap::default(),
|
||||
accent_data: None,
|
||||
accent_overrides: Vec::new(),
|
||||
fetched_tree_sitter_chunks: HashMap::default(),
|
||||
use_base_text_line_numbers: false,
|
||||
};
|
||||
@@ -2370,7 +2364,7 @@ impl Editor {
|
||||
}
|
||||
|
||||
editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
|
||||
editor.accent_data = editor.fetch_accent_data(cx);
|
||||
editor.accent_overrides = editor.fetch_accent_overrides(cx);
|
||||
|
||||
if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
|
||||
editor
|
||||
@@ -8073,17 +8067,10 @@ impl Editor {
|
||||
|
||||
if self.edit_prediction_indent_conflict {
|
||||
let cursor_point = cursor.to_point(&multibuffer);
|
||||
let mut suggested_indent = None;
|
||||
multibuffer.suggested_indents_callback(
|
||||
cursor_point.row..cursor_point.row + 1,
|
||||
|_, indent| {
|
||||
suggested_indent = Some(indent);
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
cx,
|
||||
);
|
||||
|
||||
if let Some(indent) = suggested_indent
|
||||
let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
|
||||
|
||||
if let Some((_, indent)) = indents.iter().next()
|
||||
&& indent.len == cursor_point.column
|
||||
{
|
||||
self.edit_prediction_indent_conflict = false;
|
||||
@@ -21719,18 +21706,16 @@ impl Editor {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
|
||||
fn fetch_accent_overrides(&self, cx: &App) -> Vec<SharedString> {
|
||||
if !self.mode.is_full() {
|
||||
return None;
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let theme_settings = theme::ThemeSettings::get_global(cx);
|
||||
let theme = cx.theme();
|
||||
let accent_colors = theme.accents().clone();
|
||||
|
||||
let accent_overrides = theme_settings
|
||||
theme_settings
|
||||
.theme_overrides
|
||||
.get(theme.name.as_ref())
|
||||
.get(cx.theme().name.as_ref())
|
||||
.map(|theme_style| &theme_style.accents)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
@@ -21743,12 +21728,7 @@ impl Editor {
|
||||
.flatten(),
|
||||
)
|
||||
.flat_map(|accent| accent.0.clone())
|
||||
.collect();
|
||||
|
||||
Some(AccentData {
|
||||
colors: accent_colors,
|
||||
overrides: accent_overrides,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn fetch_applicable_language_settings(
|
||||
@@ -21778,9 +21758,9 @@ impl Editor {
|
||||
let language_settings_changed = new_language_settings != self.applicable_language_settings;
|
||||
self.applicable_language_settings = new_language_settings;
|
||||
|
||||
let new_accents = self.fetch_accent_data(cx);
|
||||
let accents_changed = new_accents != self.accent_data;
|
||||
self.accent_data = new_accents;
|
||||
let new_accent_overrides = self.fetch_accent_overrides(cx);
|
||||
let accent_overrides_changed = new_accent_overrides != self.accent_overrides;
|
||||
self.accent_overrides = new_accent_overrides;
|
||||
|
||||
if self.diagnostics_enabled() {
|
||||
let new_severity = EditorSettings::get_global(cx)
|
||||
@@ -21854,7 +21834,7 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
if language_settings_changed || accents_changed {
|
||||
if language_settings_changed || accent_overrides_changed {
|
||||
self.colorize_brackets(true, cx);
|
||||
}
|
||||
|
||||
|
||||
@@ -19095,109 +19095,6 @@ async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_document_format_with_prettier_explicit_language(cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.formatter = Some(FormatterList::Single(Formatter::Prettier))
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_file(path!("/file.settings"), Default::default())
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/file.settings").as_ref()], cx).await;
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
let ts_lang = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
..LanguageMatcher::default()
|
||||
},
|
||||
prettier_parser_name: Some("typescript".to_string()),
|
||||
..LanguageConfig::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
|
||||
));
|
||||
|
||||
language_registry.add(ts_lang.clone());
|
||||
|
||||
update_test_language_settings(cx, |settings| {
|
||||
settings.defaults.prettier.get_or_insert_default().allowed = Some(true);
|
||||
});
|
||||
|
||||
let test_plugin = "test_plugin";
|
||||
let _ = language_registry.register_fake_lsp(
|
||||
"TypeScript",
|
||||
FakeLspAdapter {
|
||||
prettier_plugins: vec![test_plugin],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer(path!("/file.settings"), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
project.update(cx, |project, cx| {
|
||||
project.set_language_for_buffer(&buffer, ts_lang, cx)
|
||||
});
|
||||
|
||||
let buffer_text = "one\ntwo\nthree\n";
|
||||
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.set_text(buffer_text, window, cx)
|
||||
});
|
||||
|
||||
editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor.perform_format(
|
||||
project.clone(),
|
||||
FormatTrigger::Manual,
|
||||
FormatTarget::Buffers(editor.buffer().read(cx).all_buffers()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap()
|
||||
.await;
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.text(cx)),
|
||||
buffer_text.to_string() + prettier_format_suffix + "\ntypescript",
|
||||
"Test prettier formatting was not applied to the original buffer text",
|
||||
);
|
||||
|
||||
update_test_language_settings(cx, |settings| {
|
||||
settings.defaults.formatter = Some(FormatterList::default())
|
||||
});
|
||||
let format = editor.update_in(cx, |editor, window, cx| {
|
||||
editor.perform_format(
|
||||
project.clone(),
|
||||
FormatTrigger::Manual,
|
||||
FormatTarget::Buffers(editor.buffer().read(cx).all_buffers()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
format.await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.text(cx)),
|
||||
buffer_text.to_string()
|
||||
+ prettier_format_suffix
|
||||
+ "\ntypescript\n"
|
||||
+ prettier_format_suffix
|
||||
+ "\ntypescript",
|
||||
"Autoformatting (via test prettier) was not applied to the original buffer text",
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_addition_reverts(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
@@ -29,7 +29,6 @@ pub struct ExtensionHostProxy {
|
||||
slash_command_proxy: RwLock<Option<Arc<dyn ExtensionSlashCommandProxy>>>,
|
||||
context_server_proxy: RwLock<Option<Arc<dyn ExtensionContextServerProxy>>>,
|
||||
debug_adapter_provider_proxy: RwLock<Option<Arc<dyn ExtensionDebugAdapterProviderProxy>>>,
|
||||
language_model_provider_proxy: RwLock<Option<Arc<dyn ExtensionLanguageModelProviderProxy>>>,
|
||||
}
|
||||
|
||||
impl ExtensionHostProxy {
|
||||
@@ -55,7 +54,6 @@ impl ExtensionHostProxy {
|
||||
slash_command_proxy: RwLock::default(),
|
||||
context_server_proxy: RwLock::default(),
|
||||
debug_adapter_provider_proxy: RwLock::default(),
|
||||
language_model_provider_proxy: RwLock::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,15 +90,6 @@ impl ExtensionHostProxy {
|
||||
.write()
|
||||
.replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_language_model_provider_proxy(
|
||||
&self,
|
||||
proxy: impl ExtensionLanguageModelProviderProxy,
|
||||
) {
|
||||
self.language_model_provider_proxy
|
||||
.write()
|
||||
.replace(Arc::new(proxy));
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionThemeProxy: Send + Sync + 'static {
|
||||
@@ -386,49 +375,6 @@ pub trait ExtensionContextServerProxy: Send + Sync + 'static {
|
||||
fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App);
|
||||
}
|
||||
|
||||
/// A function that registers a language model provider with the registry.
|
||||
/// This allows extension_host to create the provider (which requires WasmExtension)
|
||||
/// and pass a registration closure to the language_models crate.
|
||||
pub type LanguageModelProviderRegistration = Box<dyn FnOnce(&mut App) + Send + Sync + 'static>;
|
||||
|
||||
pub trait ExtensionLanguageModelProviderProxy: Send + Sync + 'static {
|
||||
/// Register an LLM provider from an extension.
|
||||
/// The `register_fn` closure will be called with the App context and should
|
||||
/// register the provider with the LanguageModelRegistry.
|
||||
fn register_language_model_provider(
|
||||
&self,
|
||||
provider_id: Arc<str>,
|
||||
register_fn: LanguageModelProviderRegistration,
|
||||
cx: &mut App,
|
||||
);
|
||||
|
||||
/// Unregister an LLM provider when an extension is unloaded.
|
||||
fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App);
|
||||
}
|
||||
|
||||
impl ExtensionLanguageModelProviderProxy for ExtensionHostProxy {
|
||||
fn register_language_model_provider(
|
||||
&self,
|
||||
provider_id: Arc<str>,
|
||||
register_fn: LanguageModelProviderRegistration,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_language_model_provider(provider_id, register_fn, cx)
|
||||
}
|
||||
|
||||
fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
|
||||
let Some(proxy) = self.language_model_provider_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.unregister_language_model_provider(provider_id, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtensionContextServerProxy for ExtensionHostProxy {
|
||||
fn register_context_server(
|
||||
&self,
|
||||
|
||||
@@ -93,8 +93,6 @@ pub struct ExtensionManifest {
|
||||
pub debug_adapters: BTreeMap<Arc<str>, DebugAdapterManifestEntry>,
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub debug_locators: BTreeMap<Arc<str>, DebugLocatorManifestEntry>,
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub language_model_providers: BTreeMap<Arc<str>, LanguageModelProviderManifestEntry>,
|
||||
}
|
||||
|
||||
impl ExtensionManifest {
|
||||
@@ -290,57 +288,6 @@ pub struct DebugAdapterManifestEntry {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||
pub struct DebugLocatorManifestEntry {}
|
||||
|
||||
/// Manifest entry for a language model provider.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||
pub struct LanguageModelProviderManifestEntry {
|
||||
/// Display name for the provider.
|
||||
pub name: String,
|
||||
/// Path to an SVG icon file relative to the extension root (e.g., "icons/provider.svg").
|
||||
#[serde(default)]
|
||||
pub icon: Option<String>,
|
||||
/// Default models to show even before API connection.
|
||||
#[serde(default)]
|
||||
pub models: Vec<LanguageModelManifestEntry>,
|
||||
/// Authentication configuration.
|
||||
#[serde(default)]
|
||||
pub auth: Option<LanguageModelAuthConfig>,
|
||||
}
|
||||
|
||||
/// Manifest entry for a language model.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||
pub struct LanguageModelManifestEntry {
|
||||
/// Unique identifier for the model.
|
||||
pub id: String,
|
||||
/// Display name for the model.
|
||||
pub name: String,
|
||||
/// Maximum input token count.
|
||||
#[serde(default)]
|
||||
pub max_token_count: u64,
|
||||
/// Maximum output tokens (optional).
|
||||
#[serde(default)]
|
||||
pub max_output_tokens: Option<u64>,
|
||||
/// Whether the model supports image inputs.
|
||||
#[serde(default)]
|
||||
pub supports_images: bool,
|
||||
/// Whether the model supports tool/function calling.
|
||||
#[serde(default)]
|
||||
pub supports_tools: bool,
|
||||
/// Whether the model supports extended thinking/reasoning.
|
||||
#[serde(default)]
|
||||
pub supports_thinking: bool,
|
||||
}
|
||||
|
||||
/// Authentication configuration for a language model provider.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||
pub struct LanguageModelAuthConfig {
|
||||
/// Environment variable name for the API key.
|
||||
#[serde(default)]
|
||||
pub env_var: Option<String>,
|
||||
/// Human-readable name for the credential shown in the UI input field (e.g., "API Key", "Access Token").
|
||||
#[serde(default)]
|
||||
pub credential_label: Option<String>,
|
||||
}
|
||||
|
||||
impl ExtensionManifest {
|
||||
pub async fn load(fs: Arc<dyn Fs>, extension_dir: &Path) -> Result<Self> {
|
||||
let extension_name = extension_dir
|
||||
@@ -411,7 +358,6 @@ fn manifest_from_old_manifest(
|
||||
capabilities: Vec::new(),
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,7 +391,6 @@ mod tests {
|
||||
capabilities: vec![],
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: BTreeMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,27 +29,6 @@ pub use wit::{
|
||||
GithubRelease, GithubReleaseAsset, GithubReleaseOptions, github_release_by_tag_name,
|
||||
latest_github_release,
|
||||
},
|
||||
zed::extension::llm_provider::{
|
||||
CacheConfiguration as LlmCacheConfiguration, CompletionEvent as LlmCompletionEvent,
|
||||
CompletionRequest as LlmCompletionRequest, CredentialType as LlmCredentialType,
|
||||
ImageData as LlmImageData, MessageContent as LlmMessageContent,
|
||||
MessageRole as LlmMessageRole, ModelCapabilities as LlmModelCapabilities,
|
||||
ModelInfo as LlmModelInfo, OauthHttpRequest as LlmOauthHttpRequest,
|
||||
OauthHttpResponse as LlmOauthHttpResponse, OauthWebAuthConfig as LlmOauthWebAuthConfig,
|
||||
OauthWebAuthResult as LlmOauthWebAuthResult, ProviderInfo as LlmProviderInfo,
|
||||
RequestMessage as LlmRequestMessage, StopReason as LlmStopReason,
|
||||
ThinkingContent as LlmThinkingContent, TokenUsage as LlmTokenUsage,
|
||||
ToolChoice as LlmToolChoice, ToolDefinition as LlmToolDefinition,
|
||||
ToolInputFormat as LlmToolInputFormat, ToolResult as LlmToolResult,
|
||||
ToolResultContent as LlmToolResultContent, ToolUse as LlmToolUse,
|
||||
ToolUseJsonParseError as LlmToolUseJsonParseError,
|
||||
delete_credential as llm_delete_credential, get_credential as llm_get_credential,
|
||||
get_env_var as llm_get_env_var, oauth_open_browser as llm_oauth_open_browser,
|
||||
oauth_start_web_auth as llm_oauth_start_web_auth,
|
||||
request_credential as llm_request_credential,
|
||||
send_oauth_http_request as llm_oauth_http_request,
|
||||
store_credential as llm_store_credential,
|
||||
},
|
||||
zed::extension::nodejs::{
|
||||
node_binary_path, npm_install_package, npm_package_installed_version,
|
||||
npm_package_latest_version,
|
||||
@@ -280,81 +259,6 @@ pub trait Extension: Send + Sync {
|
||||
) -> Result<DebugRequest, String> {
|
||||
Err("`run_dap_locator` not implemented".to_string())
|
||||
}
|
||||
|
||||
/// Returns information about language model providers offered by this extension.
|
||||
fn llm_providers(&self) -> Vec<LlmProviderInfo> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Returns the models available for a provider.
|
||||
fn llm_provider_models(&self, _provider_id: &str) -> Result<Vec<LlmModelInfo>, String> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Returns markdown content to display in the provider's settings UI.
|
||||
/// This can include setup instructions, links to documentation, etc.
|
||||
fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if the provider is authenticated.
|
||||
fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Attempt to authenticate the provider.
|
||||
fn llm_provider_authenticate(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
Err("`llm_provider_authenticate` not implemented".to_string())
|
||||
}
|
||||
|
||||
/// Reset credentials for the provider.
|
||||
fn llm_provider_reset_credentials(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
Err("`llm_provider_reset_credentials` not implemented".to_string())
|
||||
}
|
||||
|
||||
/// Count tokens for a request.
|
||||
fn llm_count_tokens(
|
||||
&self,
|
||||
_provider_id: &str,
|
||||
_model_id: &str,
|
||||
_request: &LlmCompletionRequest,
|
||||
) -> Result<u64, String> {
|
||||
Err("`llm_count_tokens` not implemented".to_string())
|
||||
}
|
||||
|
||||
/// Start streaming a completion from the model.
|
||||
/// Returns a stream ID that can be used with `llm_stream_completion_next` and `llm_stream_completion_close`.
|
||||
fn llm_stream_completion_start(
|
||||
&mut self,
|
||||
_provider_id: &str,
|
||||
_model_id: &str,
|
||||
_request: &LlmCompletionRequest,
|
||||
) -> Result<String, String> {
|
||||
Err("`llm_stream_completion_start` not implemented".to_string())
|
||||
}
|
||||
|
||||
/// Get the next event from a completion stream.
|
||||
/// Returns `Ok(None)` when the stream is complete.
|
||||
fn llm_stream_completion_next(
|
||||
&mut self,
|
||||
_stream_id: &str,
|
||||
) -> Result<Option<LlmCompletionEvent>, String> {
|
||||
Err("`llm_stream_completion_next` not implemented".to_string())
|
||||
}
|
||||
|
||||
/// Close a completion stream and release its resources.
|
||||
fn llm_stream_completion_close(&mut self, _stream_id: &str) {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
|
||||
/// Get cache configuration for a model (if prompt caching is supported).
|
||||
fn llm_cache_configuration(
|
||||
&self,
|
||||
_provider_id: &str,
|
||||
_model_id: &str,
|
||||
) -> Option<LlmCacheConfiguration> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers the provided type as a Zed extension.
|
||||
@@ -614,61 +518,6 @@ impl wit::Guest for Component {
|
||||
) -> Result<DebugRequest, String> {
|
||||
extension().run_dap_locator(locator_name, build_task)
|
||||
}
|
||||
|
||||
fn llm_providers() -> Vec<LlmProviderInfo> {
|
||||
extension().llm_providers()
|
||||
}
|
||||
|
||||
fn llm_provider_models(provider_id: String) -> Result<Vec<LlmModelInfo>, String> {
|
||||
extension().llm_provider_models(&provider_id)
|
||||
}
|
||||
|
||||
fn llm_provider_settings_markdown(provider_id: String) -> Option<String> {
|
||||
extension().llm_provider_settings_markdown(&provider_id)
|
||||
}
|
||||
|
||||
fn llm_provider_is_authenticated(provider_id: String) -> bool {
|
||||
extension().llm_provider_is_authenticated(&provider_id)
|
||||
}
|
||||
|
||||
fn llm_provider_authenticate(provider_id: String) -> Result<(), String> {
|
||||
extension().llm_provider_authenticate(&provider_id)
|
||||
}
|
||||
|
||||
fn llm_provider_reset_credentials(provider_id: String) -> Result<(), String> {
|
||||
extension().llm_provider_reset_credentials(&provider_id)
|
||||
}
|
||||
|
||||
fn llm_count_tokens(
|
||||
provider_id: String,
|
||||
model_id: String,
|
||||
request: LlmCompletionRequest,
|
||||
) -> Result<u64, String> {
|
||||
extension().llm_count_tokens(&provider_id, &model_id, &request)
|
||||
}
|
||||
|
||||
fn llm_stream_completion_start(
|
||||
provider_id: String,
|
||||
model_id: String,
|
||||
request: LlmCompletionRequest,
|
||||
) -> Result<String, String> {
|
||||
extension().llm_stream_completion_start(&provider_id, &model_id, &request)
|
||||
}
|
||||
|
||||
fn llm_stream_completion_next(stream_id: String) -> Result<Option<LlmCompletionEvent>, String> {
|
||||
extension().llm_stream_completion_next(&stream_id)
|
||||
}
|
||||
|
||||
fn llm_stream_completion_close(stream_id: String) {
|
||||
extension().llm_stream_completion_close(&stream_id)
|
||||
}
|
||||
|
||||
fn llm_cache_configuration(
|
||||
provider_id: String,
|
||||
model_id: String,
|
||||
) -> Option<LlmCacheConfiguration> {
|
||||
extension().llm_cache_configuration(&provider_id, &model_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// The ID of a language server.
|
||||
|
||||
@@ -8,7 +8,6 @@ world extension {
|
||||
import platform;
|
||||
import process;
|
||||
import nodejs;
|
||||
import llm-provider;
|
||||
|
||||
use common.{env-vars, range};
|
||||
use context-server.{context-server-configuration};
|
||||
@@ -16,10 +15,6 @@ world extension {
|
||||
use lsp.{completion, symbol};
|
||||
use process.{command};
|
||||
use slash-command.{slash-command, slash-command-argument-completion, slash-command-output};
|
||||
use llm-provider.{
|
||||
provider-info, model-info, completion-request,
|
||||
credential-type, cache-configuration, completion-event, token-usage
|
||||
};
|
||||
|
||||
/// Initializes the extension.
|
||||
export init-extension: func();
|
||||
@@ -169,56 +164,4 @@ world extension {
|
||||
export dap-config-to-scenario: func(config: debug-config) -> result<debug-scenario, string>;
|
||||
export dap-locator-create-scenario: func(locator-name: string, build-config-template: build-task-template, resolved-label: string, debug-adapter-name: string) -> option<debug-scenario>;
|
||||
export run-dap-locator: func(locator-name: string, config: resolved-task) -> result<debug-request, string>;
|
||||
|
||||
/// Returns information about language model providers offered by this extension.
|
||||
export llm-providers: func() -> list<provider-info>;
|
||||
|
||||
/// Returns the models available for a provider.
|
||||
export llm-provider-models: func(provider-id: string) -> result<list<model-info>, string>;
|
||||
|
||||
/// Returns markdown content to display in the provider's settings UI.
|
||||
/// This can include setup instructions, links to documentation, etc.
|
||||
export llm-provider-settings-markdown: func(provider-id: string) -> option<string>;
|
||||
|
||||
/// Check if the provider is authenticated.
|
||||
export llm-provider-is-authenticated: func(provider-id: string) -> bool;
|
||||
|
||||
/// Attempt to authenticate the provider.
|
||||
export llm-provider-authenticate: func(provider-id: string) -> result<_, string>;
|
||||
|
||||
/// Reset credentials for the provider.
|
||||
export llm-provider-reset-credentials: func(provider-id: string) -> result<_, string>;
|
||||
|
||||
/// Count tokens for a request.
|
||||
export llm-count-tokens: func(
|
||||
provider-id: string,
|
||||
model-id: string,
|
||||
request: completion-request
|
||||
) -> result<u64, string>;
|
||||
|
||||
/// Start streaming a completion from the model.
|
||||
/// Returns a stream ID that can be used with llm-stream-next and llm-stream-close.
|
||||
export llm-stream-completion-start: func(
|
||||
provider-id: string,
|
||||
model-id: string,
|
||||
request: completion-request
|
||||
) -> result<string, string>;
|
||||
|
||||
/// Get the next event from a completion stream.
|
||||
/// Returns None when the stream is complete.
|
||||
export llm-stream-completion-next: func(
|
||||
stream-id: string
|
||||
) -> result<option<completion-event>, string>;
|
||||
|
||||
/// Close a completion stream and release its resources.
|
||||
export llm-stream-completion-close: func(
|
||||
stream-id: string
|
||||
);
|
||||
|
||||
/// Get cache configuration for a model (if prompt caching is supported).
|
||||
export llm-cache-configuration: func(
|
||||
provider-id: string,
|
||||
model-id: string
|
||||
) -> option<cache-configuration>;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,345 +0,0 @@
|
||||
interface llm-provider {
|
||||
/// Information about a language model provider.
|
||||
record provider-info {
|
||||
/// Unique identifier for the provider (e.g., "my-extension.my-provider").
|
||||
id: string,
|
||||
/// Display name for the provider.
|
||||
name: string,
|
||||
/// Path to an SVG icon file relative to the extension root (e.g., "icons/provider.svg").
|
||||
icon: option<string>,
|
||||
}
|
||||
|
||||
/// Capabilities of a language model.
|
||||
record model-capabilities {
|
||||
/// Whether the model supports image inputs.
|
||||
supports-images: bool,
|
||||
/// Whether the model supports tool/function calling.
|
||||
supports-tools: bool,
|
||||
/// Whether the model supports the "auto" tool choice.
|
||||
supports-tool-choice-auto: bool,
|
||||
/// Whether the model supports the "any" tool choice.
|
||||
supports-tool-choice-any: bool,
|
||||
/// Whether the model supports the "none" tool choice.
|
||||
supports-tool-choice-none: bool,
|
||||
/// Whether the model supports extended thinking/reasoning.
|
||||
supports-thinking: bool,
|
||||
/// The format for tool input schemas.
|
||||
tool-input-format: tool-input-format,
|
||||
}
|
||||
|
||||
/// Format for tool input schemas.
|
||||
enum tool-input-format {
|
||||
/// Standard JSON Schema format.
|
||||
json-schema,
|
||||
/// Simplified schema format for certain providers.
|
||||
simplified,
|
||||
}
|
||||
|
||||
/// Information about a specific model.
|
||||
record model-info {
|
||||
/// Unique identifier for the model.
|
||||
id: string,
|
||||
/// Display name for the model.
|
||||
name: string,
|
||||
/// Maximum input token count.
|
||||
max-token-count: u64,
|
||||
/// Maximum output tokens (optional).
|
||||
max-output-tokens: option<u64>,
|
||||
/// Model capabilities.
|
||||
capabilities: model-capabilities,
|
||||
/// Whether this is the default model for the provider.
|
||||
is-default: bool,
|
||||
/// Whether this is the default fast model.
|
||||
is-default-fast: bool,
|
||||
}
|
||||
|
||||
/// The role of a message participant.
|
||||
enum message-role {
|
||||
/// User message.
|
||||
user,
|
||||
/// Assistant message.
|
||||
assistant,
|
||||
/// System message.
|
||||
system,
|
||||
}
|
||||
|
||||
/// A message in a completion request.
|
||||
record request-message {
|
||||
/// The role of the message sender.
|
||||
role: message-role,
|
||||
/// The content of the message.
|
||||
content: list<message-content>,
|
||||
/// Whether to cache this message for prompt caching.
|
||||
cache: bool,
|
||||
}
|
||||
|
||||
/// Content within a message.
|
||||
variant message-content {
|
||||
/// Plain text content.
|
||||
text(string),
|
||||
/// Image content.
|
||||
image(image-data),
|
||||
/// A tool use request from the assistant.
|
||||
tool-use(tool-use),
|
||||
/// A tool result from the user.
|
||||
tool-result(tool-result),
|
||||
/// Thinking/reasoning content.
|
||||
thinking(thinking-content),
|
||||
/// Redacted/encrypted thinking content.
|
||||
redacted-thinking(string),
|
||||
}
|
||||
|
||||
/// Image data for vision models.
|
||||
record image-data {
|
||||
/// Base64-encoded image data.
|
||||
source: string,
|
||||
/// Image width in pixels (optional).
|
||||
width: option<u32>,
|
||||
/// Image height in pixels (optional).
|
||||
height: option<u32>,
|
||||
}
|
||||
|
||||
/// A tool use request from the model.
|
||||
record tool-use {
|
||||
/// Unique identifier for this tool use.
|
||||
id: string,
|
||||
/// The name of the tool being used.
|
||||
name: string,
|
||||
/// JSON string of the tool input arguments.
|
||||
input: string,
|
||||
/// Thought signature for providers that support it (e.g., Anthropic).
|
||||
thought-signature: option<string>,
|
||||
}
|
||||
|
||||
/// A tool result to send back to the model.
|
||||
record tool-result {
|
||||
/// The ID of the tool use this is a result for.
|
||||
tool-use-id: string,
|
||||
/// The name of the tool.
|
||||
tool-name: string,
|
||||
/// Whether this result represents an error.
|
||||
is-error: bool,
|
||||
/// The content of the result.
|
||||
content: tool-result-content,
|
||||
}
|
||||
|
||||
/// Content of a tool result.
|
||||
variant tool-result-content {
|
||||
/// Text result.
|
||||
text(string),
|
||||
/// Image result.
|
||||
image(image-data),
|
||||
}
|
||||
|
||||
/// Thinking/reasoning content from models that support extended thinking.
|
||||
record thinking-content {
|
||||
/// The thinking text.
|
||||
text: string,
|
||||
/// Signature for the thinking block (provider-specific).
|
||||
signature: option<string>,
|
||||
}
|
||||
|
||||
/// A tool definition for function calling.
|
||||
record tool-definition {
|
||||
/// The name of the tool.
|
||||
name: string,
|
||||
/// Description of what the tool does.
|
||||
description: string,
|
||||
/// JSON Schema for input parameters.
|
||||
input-schema: string,
|
||||
}
|
||||
|
||||
/// Tool choice preference for the model.
|
||||
enum tool-choice {
|
||||
/// Let the model decide whether to use tools.
|
||||
auto,
|
||||
/// Force the model to use at least one tool.
|
||||
any,
|
||||
/// Prevent the model from using tools.
|
||||
none,
|
||||
}
|
||||
|
||||
/// A completion request to send to the model.
|
||||
record completion-request {
|
||||
/// The messages in the conversation.
|
||||
messages: list<request-message>,
|
||||
/// Available tools for the model to use.
|
||||
tools: list<tool-definition>,
|
||||
/// Tool choice preference.
|
||||
tool-choice: option<tool-choice>,
|
||||
/// Stop sequences to end generation.
|
||||
stop-sequences: list<string>,
|
||||
/// Temperature for sampling (0.0-1.0).
|
||||
temperature: option<f32>,
|
||||
/// Whether thinking/reasoning is allowed.
|
||||
thinking-allowed: bool,
|
||||
/// Maximum tokens to generate.
|
||||
max-tokens: option<u64>,
|
||||
}
|
||||
|
||||
/// Events emitted during completion streaming.
|
||||
variant completion-event {
|
||||
/// Completion has started.
|
||||
started,
|
||||
/// Text content chunk.
|
||||
text(string),
|
||||
/// Thinking/reasoning content chunk.
|
||||
thinking(thinking-content),
|
||||
/// Redacted thinking (encrypted) chunk.
|
||||
redacted-thinking(string),
|
||||
/// Tool use request from the model.
|
||||
tool-use(tool-use),
|
||||
/// JSON parse error when parsing tool input.
|
||||
tool-use-json-parse-error(tool-use-json-parse-error),
|
||||
/// Completion stopped.
|
||||
stop(stop-reason),
|
||||
/// Token usage update.
|
||||
usage(token-usage),
|
||||
/// Reasoning details (provider-specific JSON).
|
||||
reasoning-details(string),
|
||||
}
|
||||
|
||||
/// Error information when tool use JSON parsing fails.
|
||||
record tool-use-json-parse-error {
|
||||
/// The tool use ID.
|
||||
id: string,
|
||||
/// The tool name.
|
||||
tool-name: string,
|
||||
/// The raw input that failed to parse.
|
||||
raw-input: string,
|
||||
/// The parse error message.
|
||||
error: string,
|
||||
}
|
||||
|
||||
/// Reason the completion stopped.
|
||||
enum stop-reason {
|
||||
/// The model finished generating.
|
||||
end-turn,
|
||||
/// Maximum tokens reached.
|
||||
max-tokens,
|
||||
/// The model wants to use a tool.
|
||||
tool-use,
|
||||
/// The model refused to respond.
|
||||
refusal,
|
||||
}
|
||||
|
||||
/// Token usage statistics.
|
||||
record token-usage {
|
||||
/// Number of input tokens used.
|
||||
input-tokens: u64,
|
||||
/// Number of output tokens generated.
|
||||
output-tokens: u64,
|
||||
/// Tokens used for cache creation (if supported).
|
||||
cache-creation-input-tokens: option<u64>,
|
||||
/// Tokens read from cache (if supported).
|
||||
cache-read-input-tokens: option<u64>,
|
||||
}
|
||||
|
||||
/// Credential types that can be requested.
|
||||
enum credential-type {
|
||||
/// An API key.
|
||||
api-key,
|
||||
/// An OAuth token.
|
||||
oauth-token,
|
||||
}
|
||||
|
||||
/// Cache configuration for prompt caching.
|
||||
record cache-configuration {
|
||||
/// Maximum number of cache anchors.
|
||||
max-cache-anchors: u32,
|
||||
/// Whether caching should be applied to tool definitions.
|
||||
should-cache-tool-definitions: bool,
|
||||
/// Minimum token count for a message to be cached.
|
||||
min-total-token-count: u64,
|
||||
}
|
||||
|
||||
/// Configuration for starting an OAuth web authentication flow.
|
||||
record oauth-web-auth-config {
|
||||
/// The URL to open in the user's browser to start authentication.
|
||||
/// This should include client_id, redirect_uri, scope, state, etc.
|
||||
auth-url: string,
|
||||
/// The path to listen on for the OAuth callback (e.g., "/callback").
|
||||
/// A localhost server will be started to receive the redirect.
|
||||
callback-path: string,
|
||||
/// Timeout in seconds to wait for the callback (default: 300 = 5 minutes).
|
||||
timeout-secs: option<u32>,
|
||||
}
|
||||
|
||||
/// Result of an OAuth web authentication flow.
|
||||
record oauth-web-auth-result {
|
||||
/// The full callback URL that was received, including query parameters.
|
||||
/// The extension is responsible for parsing the code, state, etc.
|
||||
callback-url: string,
|
||||
/// The port that was used for the localhost callback server.
|
||||
port: u32,
|
||||
}
|
||||
|
||||
/// A generic HTTP request for OAuth token exchange.
|
||||
record oauth-http-request {
|
||||
/// The URL to request.
|
||||
url: string,
|
||||
/// HTTP method (e.g., "POST", "GET").
|
||||
method: string,
|
||||
/// Request headers as key-value pairs.
|
||||
headers: list<tuple<string, string>>,
|
||||
/// Request body as a string (for form-encoded or JSON bodies).
|
||||
body: string,
|
||||
}
|
||||
|
||||
/// Response from an OAuth HTTP request.
|
||||
record oauth-http-response {
|
||||
/// HTTP status code.
|
||||
status: u16,
|
||||
/// Response headers as key-value pairs.
|
||||
headers: list<tuple<string, string>>,
|
||||
/// Response body as a string.
|
||||
body: string,
|
||||
}
|
||||
|
||||
/// Request a credential from the user.
|
||||
/// Returns true if the credential was provided, false if the user cancelled.
|
||||
request-credential: func(
|
||||
provider-id: string,
|
||||
credential-type: credential-type,
|
||||
label: string,
|
||||
placeholder: string
|
||||
) -> result<bool, string>;
|
||||
|
||||
/// Get a stored credential for this provider.
|
||||
get-credential: func(provider-id: string) -> option<string>;
|
||||
|
||||
/// Store a credential for this provider.
|
||||
store-credential: func(provider-id: string, value: string) -> result<_, string>;
|
||||
|
||||
/// Delete a stored credential for this provider.
|
||||
delete-credential: func(provider-id: string) -> result<_, string>;
|
||||
|
||||
/// Read an environment variable.
|
||||
get-env-var: func(name: string) -> option<string>;
|
||||
|
||||
/// Start an OAuth web authentication flow.
|
||||
///
|
||||
/// This will:
|
||||
/// 1. Start a localhost server to receive the OAuth callback
|
||||
/// 2. Open the auth URL in the user's default browser
|
||||
/// 3. Wait for the callback (up to the timeout)
|
||||
/// 4. Return the callback URL with query parameters
|
||||
///
|
||||
/// The extension is responsible for:
|
||||
/// - Constructing the auth URL with client_id, redirect_uri, scope, state, etc.
|
||||
/// - Parsing the callback URL to extract the authorization code
|
||||
/// - Exchanging the code for tokens using oauth-http-request
|
||||
oauth-start-web-auth: func(config: oauth-web-auth-config) -> result<oauth-web-auth-result, string>;
|
||||
|
||||
/// Make an HTTP request for OAuth token exchange.
|
||||
///
|
||||
/// This is a simple HTTP client for OAuth flows, allowing the extension
|
||||
/// to handle token exchange with full control over serialization.
|
||||
send-oauth-http-request: func(request: oauth-http-request) -> result<oauth-http-response, string>;
|
||||
|
||||
/// Open a URL in the user's default browser.
|
||||
///
|
||||
/// Useful for OAuth flows that need to open a browser but handle the
|
||||
/// callback differently (e.g., polling-based flows).
|
||||
oauth-open-browser: func(url: string) -> result<_, string>;
|
||||
}
|
||||
@@ -254,21 +254,6 @@ async fn copy_extension_resources(
|
||||
}
|
||||
}
|
||||
|
||||
for (_, provider_entry) in &manifest.language_model_providers {
|
||||
if let Some(icon_path) = &provider_entry.icon {
|
||||
let source_icon = extension_path.join(icon_path);
|
||||
let dest_icon = output_dir.join(icon_path);
|
||||
|
||||
// Create parent directory if needed
|
||||
if let Some(parent) = dest_icon.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
fs::copy(&source_icon, &dest_icon)
|
||||
.with_context(|| format!("failed to copy LLM provider icon '{}'", icon_path))?;
|
||||
}
|
||||
}
|
||||
|
||||
if !manifest.languages.is_empty() {
|
||||
let output_languages_dir = output_dir.join("languages");
|
||||
fs::create_dir_all(&output_languages_dir)?;
|
||||
|
||||
@@ -22,10 +22,7 @@ async-tar.workspace = true
|
||||
async-trait.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
credentials_provider.workspace = true
|
||||
dap.workspace = true
|
||||
dirs.workspace = true
|
||||
editor.workspace = true
|
||||
extension.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
@@ -33,11 +30,8 @@ gpui.workspace = true
|
||||
gpui_tokio.workspace = true
|
||||
http_client.workspace = true
|
||||
language.workspace = true
|
||||
language_model.workspace = true
|
||||
log.workspace = true
|
||||
markdown.workspace = true
|
||||
lsp.workspace = true
|
||||
menu.workspace = true
|
||||
moka.workspace = true
|
||||
node_runtime.workspace = true
|
||||
paths.workspace = true
|
||||
@@ -49,13 +43,10 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_json_lenient.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
task.workspace = true
|
||||
telemetry.workspace = true
|
||||
tempfile.workspace = true
|
||||
theme.workspace = true
|
||||
toml.workspace = true
|
||||
ui.workspace = true
|
||||
url.workspace = true
|
||||
util.workspace = true
|
||||
wasmparser.workspace = true
|
||||
|
||||
@@ -143,7 +143,6 @@ fn manifest() -> ExtensionManifest {
|
||||
)],
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: BTreeMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,6 @@ mod tests {
|
||||
capabilities: vec![],
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: BTreeMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
use credentials_provider::CredentialsProvider;
|
||||
use gpui::App;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const COPILOT_CHAT_EXTENSION_ID: &str = "copilot_chat";
|
||||
const COPILOT_CHAT_PROVIDER_ID: &str = "copilot_chat";
|
||||
|
||||
pub fn migrate_copilot_credentials_if_needed(extension_id: &str, cx: &mut App) {
|
||||
if extension_id != COPILOT_CHAT_EXTENSION_ID {
|
||||
return;
|
||||
}
|
||||
|
||||
let credential_key = format!(
|
||||
"extension-llm-{}:{}",
|
||||
COPILOT_CHAT_EXTENSION_ID, COPILOT_CHAT_PROVIDER_ID
|
||||
);
|
||||
|
||||
let credentials_provider = <dyn CredentialsProvider>::global(cx);
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let existing_credential = credentials_provider
|
||||
.read_credentials(&credential_key, &cx)
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
if existing_credential.is_some() {
|
||||
log::debug!("Copilot Chat extension already has credentials, skipping migration");
|
||||
return;
|
||||
}
|
||||
|
||||
let oauth_token = match read_copilot_oauth_token().await {
|
||||
Some(token) => token,
|
||||
None => {
|
||||
log::debug!("No existing Copilot OAuth token found to migrate");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!("Migrating existing Copilot OAuth token to Copilot Chat extension");
|
||||
|
||||
match credentials_provider
|
||||
.write_credentials(&credential_key, "api_key", oauth_token.as_bytes(), &cx)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
log::info!("Successfully migrated Copilot OAuth token to Copilot Chat extension");
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to migrate Copilot OAuth token: {}", err);
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
async fn read_copilot_oauth_token() -> Option<String> {
|
||||
let config_paths = copilot_config_paths();
|
||||
|
||||
for path in config_paths {
|
||||
if let Some(token) = read_oauth_token_from_file(&path).await {
|
||||
return Some(token);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn copilot_config_paths() -> Vec<PathBuf> {
|
||||
let config_dir = if cfg!(target_os = "windows") {
|
||||
dirs::data_local_dir()
|
||||
} else {
|
||||
std::env::var("XDG_CONFIG_HOME")
|
||||
.map(PathBuf::from)
|
||||
.ok()
|
||||
.or_else(|| dirs::home_dir().map(|h| h.join(".config")))
|
||||
};
|
||||
|
||||
let Some(config_dir) = config_dir else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let copilot_dir = config_dir.join("github-copilot");
|
||||
|
||||
vec![
|
||||
copilot_dir.join("hosts.json"),
|
||||
copilot_dir.join("apps.json"),
|
||||
]
|
||||
}
|
||||
|
||||
async fn read_oauth_token_from_file(path: &PathBuf) -> Option<String> {
|
||||
let contents = match smol::fs::read_to_string(path).await {
|
||||
Ok(contents) => contents,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
extract_oauth_token(&contents, "github.com")
|
||||
}
|
||||
|
||||
fn extract_oauth_token(contents: &str, domain: &str) -> Option<String> {
|
||||
let value: serde_json::Value = serde_json::from_str(contents).ok()?;
|
||||
let obj = value.as_object()?;
|
||||
|
||||
for (key, value) in obj.iter() {
|
||||
if key.starts_with(domain) {
|
||||
if let Some(token) = value.get("oauth_token").and_then(|v| v.as_str()) {
|
||||
return Some(token.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_oauth_token() {
|
||||
let contents = r#"{
|
||||
"github.com": {
|
||||
"oauth_token": "ghu_test_token_12345"
|
||||
}
|
||||
}"#;
|
||||
|
||||
let token = extract_oauth_token(contents, "github.com");
|
||||
assert_eq!(token, Some("ghu_test_token_12345".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_oauth_token_with_prefix() {
|
||||
let contents = r#"{
|
||||
"github.com:user": {
|
||||
"oauth_token": "ghu_another_token"
|
||||
}
|
||||
}"#;
|
||||
|
||||
let token = extract_oauth_token(contents, "github.com");
|
||||
assert_eq!(token, Some("ghu_another_token".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_oauth_token_missing() {
|
||||
let contents = r#"{
|
||||
"gitlab.com": {
|
||||
"oauth_token": "some_token"
|
||||
}
|
||||
}"#;
|
||||
|
||||
let token = extract_oauth_token(contents, "github.com");
|
||||
assert_eq!(token, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_oauth_token_invalid_json() {
|
||||
let contents = "not valid json";
|
||||
let token = extract_oauth_token(contents, "github.com");
|
||||
assert_eq!(token, None);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
mod capability_granter;
|
||||
mod copilot_migration;
|
||||
pub mod extension_settings;
|
||||
pub mod headless_host;
|
||||
pub mod wasm_host;
|
||||
@@ -17,9 +16,9 @@ pub use extension::ExtensionManifest;
|
||||
use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
|
||||
use extension::{
|
||||
ExtensionContextServerProxy, ExtensionDebugAdapterProviderProxy, ExtensionEvents,
|
||||
ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageModelProviderProxy,
|
||||
ExtensionLanguageProxy, ExtensionLanguageServerProxy, ExtensionSlashCommandProxy,
|
||||
ExtensionSnippetProxy, ExtensionThemeProxy,
|
||||
ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageProxy,
|
||||
ExtensionLanguageServerProxy, ExtensionSlashCommandProxy, ExtensionSnippetProxy,
|
||||
ExtensionThemeProxy,
|
||||
};
|
||||
use fs::{Fs, RemoveOptions};
|
||||
use futures::future::join_all;
|
||||
@@ -33,8 +32,8 @@ use futures::{
|
||||
select_biased,
|
||||
};
|
||||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
|
||||
WeakEntity, actions,
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, Task, WeakEntity,
|
||||
actions,
|
||||
};
|
||||
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use language::{
|
||||
@@ -58,20 +57,11 @@ use std::{
|
||||
};
|
||||
use url::Url;
|
||||
use util::{ResultExt, paths::RemotePathBuf};
|
||||
use wasm_host::llm_provider::ExtensionLanguageModelProvider;
|
||||
use wasm_host::{
|
||||
WasmExtension, WasmHost,
|
||||
wit::{LlmModelInfo, LlmProviderInfo, is_supported_wasm_api_version, wasm_api_version_range},
|
||||
wit::{is_supported_wasm_api_version, wasm_api_version_range},
|
||||
};
|
||||
|
||||
struct LlmProviderWithModels {
|
||||
provider_info: LlmProviderInfo,
|
||||
models: Vec<LlmModelInfo>,
|
||||
is_authenticated: bool,
|
||||
icon_path: Option<SharedString>,
|
||||
auth_config: Option<extension::LanguageModelAuthConfig>,
|
||||
}
|
||||
|
||||
pub use extension::{
|
||||
ExtensionLibraryKind, GrammarManifestEntry, OldExtensionManifest, SchemaVersion,
|
||||
};
|
||||
@@ -80,80 +70,6 @@ pub use extension_settings::ExtensionSettings;
|
||||
pub const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
|
||||
const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
|
||||
|
||||
/// Extension IDs that are being migrated from hardcoded LLM providers.
|
||||
/// For backwards compatibility, if the user has the corresponding env var set,
|
||||
/// we automatically enable env var reading for these extensions.
|
||||
const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[
|
||||
"anthropic",
|
||||
"copilot_chat",
|
||||
"google-ai",
|
||||
"open_router",
|
||||
"openai",
|
||||
];
|
||||
|
||||
/// Migrates legacy LLM provider extensions by auto-enabling env var reading
|
||||
/// if the env var is currently present in the environment.
|
||||
fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut App) {
|
||||
// Only apply migration to known legacy LLM extensions
|
||||
if !LEGACY_LLM_EXTENSION_IDS.contains(&manifest.id.as_ref()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check each provider in the manifest
|
||||
for (provider_id, provider_entry) in &manifest.language_model_providers {
|
||||
let Some(auth_config) = &provider_entry.auth else {
|
||||
continue;
|
||||
};
|
||||
let Some(env_var_name) = &auth_config.env_var else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Check if the env var is present and non-empty
|
||||
let env_var_is_set = std::env::var(env_var_name)
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false);
|
||||
|
||||
if !env_var_is_set {
|
||||
continue;
|
||||
}
|
||||
|
||||
let full_provider_id: Arc<str> = format!("{}:{}", manifest.id, provider_id).into();
|
||||
|
||||
// Check if already in settings
|
||||
let already_allowed = ExtensionSettings::get_global(cx)
|
||||
.allowed_env_var_providers
|
||||
.contains(full_provider_id.as_ref());
|
||||
|
||||
if already_allowed {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Auto-enable env var reading for this provider
|
||||
log::info!(
|
||||
"Migrating legacy LLM provider {}: auto-enabling {} env var reading",
|
||||
full_provider_id,
|
||||
env_var_name
|
||||
);
|
||||
|
||||
settings::update_settings_file(<dyn fs::Fs>::global(cx), cx, {
|
||||
let full_provider_id = full_provider_id.clone();
|
||||
move |settings, _| {
|
||||
let providers = settings
|
||||
.extension
|
||||
.allowed_env_var_providers
|
||||
.get_or_insert_with(Vec::new);
|
||||
|
||||
if !providers
|
||||
.iter()
|
||||
.any(|id| id.as_ref() == full_provider_id.as_ref())
|
||||
{
|
||||
providers.push(full_provider_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The current extension [`SchemaVersion`] supported by Zed.
|
||||
const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
|
||||
|
||||
@@ -855,11 +771,6 @@ impl ExtensionStore {
|
||||
|
||||
if let ExtensionOperation::Install = operation {
|
||||
this.update(cx, |this, cx| {
|
||||
// Check for legacy LLM provider migration
|
||||
if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
|
||||
migrate_legacy_llm_provider_env_var(&manifest, cx);
|
||||
}
|
||||
|
||||
cx.emit(Event::ExtensionInstalled(extension_id.clone()));
|
||||
if let Some(events) = ExtensionEvents::try_global(cx)
|
||||
&& let Some(manifest) = this.extension_manifest_for_id(&extension_id)
|
||||
@@ -868,9 +779,6 @@ impl ExtensionStore {
|
||||
this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
|
||||
});
|
||||
}
|
||||
|
||||
// Run extension-specific migrations
|
||||
copilot_migration::migrate_copilot_credentials_if_needed(&extension_id, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
@@ -1309,11 +1217,6 @@ impl ExtensionStore {
|
||||
for command_name in extension.manifest.slash_commands.keys() {
|
||||
self.proxy.unregister_slash_command(command_name.clone());
|
||||
}
|
||||
for provider_id in extension.manifest.language_model_providers.keys() {
|
||||
let full_provider_id: Arc<str> = format!("{}:{}", extension_id, provider_id).into();
|
||||
self.proxy
|
||||
.unregister_language_model_provider(full_provider_id, cx);
|
||||
}
|
||||
}
|
||||
|
||||
self.wasm_extensions
|
||||
@@ -1452,11 +1355,7 @@ impl ExtensionStore {
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut wasm_extensions: Vec<(
|
||||
Arc<ExtensionManifest>,
|
||||
WasmExtension,
|
||||
Vec<LlmProviderWithModels>,
|
||||
)> = Vec::new();
|
||||
let mut wasm_extensions = Vec::new();
|
||||
for extension in extension_entries {
|
||||
if extension.manifest.lib.kind.is_none() {
|
||||
continue;
|
||||
@@ -1474,122 +1373,7 @@ impl ExtensionStore {
|
||||
|
||||
match wasm_extension {
|
||||
Ok(wasm_extension) => {
|
||||
// Query for LLM providers if the manifest declares any
|
||||
let mut llm_providers_with_models = Vec::new();
|
||||
if !extension.manifest.language_model_providers.is_empty() {
|
||||
let providers_result = wasm_extension
|
||||
.call(|ext, store| {
|
||||
async move { ext.call_llm_providers(store).await }.boxed()
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Ok(Ok(providers)) = providers_result {
|
||||
for provider_info in providers {
|
||||
let models_result = wasm_extension
|
||||
.call({
|
||||
let provider_id = provider_info.id.clone();
|
||||
|ext, store| {
|
||||
async move {
|
||||
ext.call_llm_provider_models(store, &provider_id)
|
||||
.await
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let models: Vec<LlmModelInfo> = match models_result {
|
||||
Ok(Ok(Ok(models))) => models,
|
||||
Ok(Ok(Err(e))) => {
|
||||
log::error!(
|
||||
"Failed to get models for LLM provider {} in extension {}: {}",
|
||||
provider_info.id,
|
||||
extension.manifest.id,
|
||||
e
|
||||
);
|
||||
Vec::new()
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
log::error!(
|
||||
"Wasm error calling llm_provider_models for {} in extension {}: {:?}",
|
||||
provider_info.id,
|
||||
extension.manifest.id,
|
||||
e
|
||||
);
|
||||
Vec::new()
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Extension call failed for llm_provider_models {} in extension {}: {:?}",
|
||||
provider_info.id,
|
||||
extension.manifest.id,
|
||||
e
|
||||
);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
// Query initial authentication state
|
||||
let is_authenticated = wasm_extension
|
||||
.call({
|
||||
let provider_id = provider_info.id.clone();
|
||||
|ext, store| {
|
||||
async move {
|
||||
ext.call_llm_provider_is_authenticated(
|
||||
store,
|
||||
&provider_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap_or(Ok(false))
|
||||
.unwrap_or(false);
|
||||
|
||||
// Resolve icon path if provided
|
||||
let icon_path = provider_info.icon.as_ref().map(|icon| {
|
||||
let icon_file_path = extension_path.join(icon);
|
||||
// Canonicalize to resolve symlinks (dev extensions are symlinked)
|
||||
let absolute_icon_path = icon_file_path
|
||||
.canonicalize()
|
||||
.unwrap_or(icon_file_path)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
SharedString::from(absolute_icon_path)
|
||||
});
|
||||
|
||||
let provider_id_arc: Arc<str> =
|
||||
provider_info.id.as_str().into();
|
||||
let auth_config = extension
|
||||
.manifest
|
||||
.language_model_providers
|
||||
.get(&provider_id_arc)
|
||||
.and_then(|entry| entry.auth.clone());
|
||||
|
||||
llm_providers_with_models.push(LlmProviderWithModels {
|
||||
provider_info,
|
||||
models,
|
||||
is_authenticated,
|
||||
icon_path,
|
||||
auth_config,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
log::error!(
|
||||
"Failed to get LLM providers from extension {}: {:?}",
|
||||
extension.manifest.id,
|
||||
providers_result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
wasm_extensions.push((
|
||||
extension.manifest.clone(),
|
||||
wasm_extension,
|
||||
llm_providers_with_models,
|
||||
))
|
||||
wasm_extensions.push((extension.manifest.clone(), wasm_extension))
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
@@ -1608,7 +1392,7 @@ impl ExtensionStore {
|
||||
this.update(cx, |this, cx| {
|
||||
this.reload_complete_senders.clear();
|
||||
|
||||
for (manifest, wasm_extension, llm_providers_with_models) in &wasm_extensions {
|
||||
for (manifest, wasm_extension) in &wasm_extensions {
|
||||
let extension = Arc::new(wasm_extension.clone());
|
||||
|
||||
for (language_server_id, language_server_config) in &manifest.language_servers {
|
||||
@@ -1662,41 +1446,9 @@ impl ExtensionStore {
|
||||
this.proxy
|
||||
.register_debug_locator(extension.clone(), debug_adapter.clone());
|
||||
}
|
||||
|
||||
// Register LLM providers
|
||||
for llm_provider in llm_providers_with_models {
|
||||
let provider_id: Arc<str> =
|
||||
format!("{}:{}", manifest.id, llm_provider.provider_info.id).into();
|
||||
let wasm_ext = extension.as_ref().clone();
|
||||
let pinfo = llm_provider.provider_info.clone();
|
||||
let mods = llm_provider.models.clone();
|
||||
let auth = llm_provider.is_authenticated;
|
||||
let icon = llm_provider.icon_path.clone();
|
||||
let auth_config = llm_provider.auth_config.clone();
|
||||
|
||||
this.proxy.register_language_model_provider(
|
||||
provider_id.clone(),
|
||||
Box::new(move |cx: &mut App| {
|
||||
let provider = Arc::new(ExtensionLanguageModelProvider::new(
|
||||
wasm_ext, pinfo, mods, auth, icon, auth_config, cx,
|
||||
));
|
||||
language_model::LanguageModelRegistry::global(cx).update(
|
||||
cx,
|
||||
|registry, cx| {
|
||||
registry.register_provider(provider, cx);
|
||||
},
|
||||
);
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let wasm_extensions_without_llm: Vec<_> = wasm_extensions
|
||||
.into_iter()
|
||||
.map(|(manifest, ext, _)| (manifest, ext))
|
||||
.collect();
|
||||
this.wasm_extensions.extend(wasm_extensions_without_llm);
|
||||
this.wasm_extensions.extend(wasm_extensions);
|
||||
this.proxy.set_extensions_loaded();
|
||||
this.proxy.reload_current_theme(cx);
|
||||
this.proxy.reload_current_icon_theme(cx);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use collections::{HashMap, HashSet};
|
||||
use collections::HashMap;
|
||||
use extension::{
|
||||
DownloadFileCapability, ExtensionCapability, NpmInstallPackageCapability, ProcessExecCapability,
|
||||
};
|
||||
@@ -16,10 +16,6 @@ pub struct ExtensionSettings {
|
||||
pub auto_install_extensions: HashMap<Arc<str>, bool>,
|
||||
pub auto_update_extensions: HashMap<Arc<str>, bool>,
|
||||
pub granted_capabilities: Vec<ExtensionCapability>,
|
||||
/// The extension language model providers that are allowed to read API keys
|
||||
/// from environment variables. Each entry is a provider ID in the format
|
||||
/// "extension_id:provider_id".
|
||||
pub allowed_env_var_providers: HashSet<Arc<str>>,
|
||||
}
|
||||
|
||||
impl ExtensionSettings {
|
||||
@@ -64,13 +60,6 @@ impl Settings for ExtensionSettings {
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
allowed_env_var_providers: content
|
||||
.extension
|
||||
.allowed_env_var_providers
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
capabilities: Vec::new(),
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: BTreeMap::default(),
|
||||
}),
|
||||
dev: false,
|
||||
},
|
||||
@@ -197,7 +196,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
capabilities: Vec::new(),
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: BTreeMap::default(),
|
||||
}),
|
||||
dev: false,
|
||||
},
|
||||
@@ -378,7 +376,6 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
capabilities: Vec::new(),
|
||||
debug_adapters: Default::default(),
|
||||
debug_locators: Default::default(),
|
||||
language_model_providers: BTreeMap::default(),
|
||||
}),
|
||||
dev: false,
|
||||
},
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
pub mod llm_provider;
|
||||
pub mod wit;
|
||||
|
||||
use crate::capability_granter::CapabilityGranter;
|
||||
use crate::{ExtensionManifest, ExtensionSettings};
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use async_trait::async_trait;
|
||||
use collections::HashSet;
|
||||
use dap::{DebugRequest, StartDebuggingRequestArgumentsRequest};
|
||||
use extension::{
|
||||
CodeLabel, Command, Completion, ContextServerConfiguration, DebugAdapterBinary,
|
||||
@@ -60,8 +58,6 @@ pub struct WasmHost {
|
||||
pub work_dir: PathBuf,
|
||||
/// The capabilities granted to extensions running on the host.
|
||||
pub(crate) granted_capabilities: Vec<ExtensionCapability>,
|
||||
/// Extension LLM providers allowed to read API keys from environment variables.
|
||||
pub(crate) allowed_env_var_providers: HashSet<Arc<str>>,
|
||||
_main_thread_message_task: Task<()>,
|
||||
main_thread_message_tx: mpsc::UnboundedSender<MainThreadCall>,
|
||||
}
|
||||
@@ -76,6 +72,12 @@ pub struct WasmExtension {
|
||||
_task: Arc<Task<Result<(), gpui_tokio::JoinError>>>,
|
||||
}
|
||||
|
||||
impl Drop for WasmExtension {
|
||||
fn drop(&mut self) {
|
||||
self.tx.close_channel();
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl extension::Extension for WasmExtension {
|
||||
fn manifest(&self) -> Arc<ExtensionManifest> {
|
||||
@@ -588,7 +590,6 @@ impl WasmHost {
|
||||
proxy,
|
||||
release_channel: ReleaseChannel::global(cx),
|
||||
granted_capabilities: extension_settings.granted_capabilities.clone(),
|
||||
allowed_env_var_providers: extension_settings.allowed_env_var_providers.clone(),
|
||||
_main_thread_message_task: task,
|
||||
main_thread_message_tx: tx,
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ use lsp::LanguageServerName;
|
||||
use release_channel::ReleaseChannel;
|
||||
use task::{DebugScenario, SpawnInTerminal, TaskTemplate, ZedDebugConfig};
|
||||
|
||||
use crate::wasm_host::wit::since_v0_8_0::dap::StartDebuggingRequestArgumentsRequest;
|
||||
use crate::wasm_host::wit::since_v0_6_0::dap::StartDebuggingRequestArgumentsRequest;
|
||||
|
||||
use super::{WasmState, wasm_engine};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
@@ -33,19 +33,6 @@ pub use latest::CodeLabelSpanLiteral;
|
||||
pub use latest::{
|
||||
CodeLabel, CodeLabelSpan, Command, DebugAdapterBinary, ExtensionProject, Range, SlashCommand,
|
||||
zed::extension::context_server::ContextServerConfiguration,
|
||||
zed::extension::llm_provider::{
|
||||
CacheConfiguration as LlmCacheConfiguration, CompletionEvent as LlmCompletionEvent,
|
||||
CompletionRequest as LlmCompletionRequest, CredentialType as LlmCredentialType,
|
||||
ImageData as LlmImageData, MessageContent as LlmMessageContent,
|
||||
MessageRole as LlmMessageRole, ModelCapabilities as LlmModelCapabilities,
|
||||
ModelInfo as LlmModelInfo, ProviderInfo as LlmProviderInfo,
|
||||
RequestMessage as LlmRequestMessage, StopReason as LlmStopReason,
|
||||
ThinkingContent as LlmThinkingContent, TokenUsage as LlmTokenUsage,
|
||||
ToolChoice as LlmToolChoice, ToolDefinition as LlmToolDefinition,
|
||||
ToolInputFormat as LlmToolInputFormat, ToolResult as LlmToolResult,
|
||||
ToolResultContent as LlmToolResultContent, ToolUse as LlmToolUse,
|
||||
ToolUseJsonParseError as LlmToolUseJsonParseError,
|
||||
},
|
||||
zed::extension::lsp::{
|
||||
Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
|
||||
},
|
||||
@@ -80,7 +67,7 @@ pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive
|
||||
|
||||
let max_version = match release_channel {
|
||||
ReleaseChannel::Dev | ReleaseChannel::Nightly => latest::MAX_VERSION,
|
||||
ReleaseChannel::Stable | ReleaseChannel::Preview => latest::MAX_VERSION,
|
||||
ReleaseChannel::Stable | ReleaseChannel::Preview => since_v0_6_0::MAX_VERSION,
|
||||
};
|
||||
|
||||
since_v0_0_1::MIN_VERSION..=max_version
|
||||
@@ -1010,6 +997,7 @@ impl Extension {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_get_dap_binary(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
@@ -1019,20 +1007,6 @@ impl Extension {
|
||||
resource: Resource<Arc<dyn WorktreeDelegate>>,
|
||||
) -> Result<Result<DebugAdapterBinary, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
let dap_binary = ext
|
||||
.call_get_dap_binary(
|
||||
store,
|
||||
&adapter_name,
|
||||
&task.try_into()?,
|
||||
user_installed_path.as_ref().and_then(|p| p.to_str()),
|
||||
resource,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| anyhow!("{e:?}"))?;
|
||||
|
||||
Ok(Ok(dap_binary))
|
||||
}
|
||||
Extension::V0_6_0(ext) => {
|
||||
let dap_binary = ext
|
||||
.call_get_dap_binary(
|
||||
@@ -1050,6 +1024,7 @@ impl Extension {
|
||||
_ => anyhow::bail!("`get_dap_binary` not available prior to v0.6.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_dap_request_kind(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
@@ -1057,16 +1032,6 @@ impl Extension {
|
||||
config: serde_json::Value,
|
||||
) -> Result<Result<StartDebuggingRequestArgumentsRequest, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
let config =
|
||||
serde_json::to_string(&config).context("Adapter config is not a valid JSON")?;
|
||||
let result = ext
|
||||
.call_dap_request_kind(store, &adapter_name, &config)
|
||||
.await?
|
||||
.map_err(|e| anyhow!("{e:?}"))?;
|
||||
|
||||
Ok(Ok(result))
|
||||
}
|
||||
Extension::V0_6_0(ext) => {
|
||||
let config =
|
||||
serde_json::to_string(&config).context("Adapter config is not a valid JSON")?;
|
||||
@@ -1080,21 +1045,13 @@ impl Extension {
|
||||
_ => anyhow::bail!("`dap_request_kind` not available prior to v0.6.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_dap_config_to_scenario(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
config: ZedDebugConfig,
|
||||
) -> Result<Result<DebugScenario, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
let config = config.into();
|
||||
let result = ext
|
||||
.call_dap_config_to_scenario(store, &config)
|
||||
.await?
|
||||
.map_err(|e| anyhow!("{e:?}"))?;
|
||||
|
||||
Ok(Ok(result.try_into()?))
|
||||
}
|
||||
Extension::V0_6_0(ext) => {
|
||||
let config = config.into();
|
||||
let dap_binary = ext
|
||||
@@ -1107,6 +1064,7 @@ impl Extension {
|
||||
_ => anyhow::bail!("`dap_config_to_scenario` not available prior to v0.6.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_dap_locator_create_scenario(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
@@ -1116,20 +1074,6 @@ impl Extension {
|
||||
debug_adapter_name: String,
|
||||
) -> Result<Option<DebugScenario>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
let build_config_template = build_config_template.into();
|
||||
let result = ext
|
||||
.call_dap_locator_create_scenario(
|
||||
store,
|
||||
&locator_name,
|
||||
&build_config_template,
|
||||
&resolved_label,
|
||||
&debug_adapter_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(TryInto::try_into).transpose()?)
|
||||
}
|
||||
Extension::V0_6_0(ext) => {
|
||||
let build_config_template = build_config_template.into();
|
||||
let dap_binary = ext
|
||||
@@ -1147,6 +1091,7 @@ impl Extension {
|
||||
_ => anyhow::bail!("`dap_locator_create_scenario` not available prior to v0.6.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_run_dap_locator(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
@@ -1154,15 +1099,6 @@ impl Extension {
|
||||
resolved_build_task: SpawnInTerminal,
|
||||
) -> Result<Result<DebugRequest, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
let build_config_template = resolved_build_task.try_into()?;
|
||||
let dap_request = ext
|
||||
.call_run_dap_locator(store, &locator_name, &build_config_template)
|
||||
.await?
|
||||
.map_err(|e| anyhow!("{e:?}"))?;
|
||||
|
||||
Ok(Ok(dap_request.into()))
|
||||
}
|
||||
Extension::V0_6_0(ext) => {
|
||||
let build_config_template = resolved_build_task.try_into()?;
|
||||
let dap_request = ext
|
||||
@@ -1175,149 +1111,6 @@ impl Extension {
|
||||
_ => anyhow::bail!("`dap_locator_create_scenario` not available prior to v0.6.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_providers(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
) -> Result<Vec<latest::llm_provider::ProviderInfo>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => ext.call_llm_providers(store).await,
|
||||
_ => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_provider_models(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
) -> Result<Result<Vec<latest::llm_provider::ModelInfo>, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => ext.call_llm_provider_models(store, provider_id).await,
|
||||
_ => anyhow::bail!("`llm_provider_models` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_provider_settings_markdown(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
) -> Result<Option<String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
ext.call_llm_provider_settings_markdown(store, provider_id)
|
||||
.await
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_provider_is_authenticated(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
) -> Result<bool> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
ext.call_llm_provider_is_authenticated(store, provider_id)
|
||||
.await
|
||||
}
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_provider_authenticate(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
) -> Result<Result<(), String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => ext.call_llm_provider_authenticate(store, provider_id).await,
|
||||
_ => anyhow::bail!("`llm_provider_authenticate` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_provider_reset_credentials(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
) -> Result<Result<(), String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
ext.call_llm_provider_reset_credentials(store, provider_id)
|
||||
.await
|
||||
}
|
||||
_ => anyhow::bail!("`llm_provider_reset_credentials` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_count_tokens(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
request: &latest::llm_provider::CompletionRequest,
|
||||
) -> Result<Result<u64, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
ext.call_llm_count_tokens(store, provider_id, model_id, request)
|
||||
.await
|
||||
}
|
||||
_ => anyhow::bail!("`llm_count_tokens` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_stream_completion_start(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
request: &latest::llm_provider::CompletionRequest,
|
||||
) -> Result<Result<String, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
ext.call_llm_stream_completion_start(store, provider_id, model_id, request)
|
||||
.await
|
||||
}
|
||||
_ => anyhow::bail!("`llm_stream_completion_start` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_stream_completion_next(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
stream_id: &str,
|
||||
) -> Result<Result<Option<latest::llm_provider::CompletionEvent>, String>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => ext.call_llm_stream_completion_next(store, stream_id).await,
|
||||
_ => anyhow::bail!("`llm_stream_completion_next` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_stream_completion_close(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
stream_id: &str,
|
||||
) -> Result<()> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => ext.call_llm_stream_completion_close(store, stream_id).await,
|
||||
_ => anyhow::bail!("`llm_stream_completion_close` not available prior to v0.8.0"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_llm_cache_configuration(
|
||||
&self,
|
||||
store: &mut Store<WasmState>,
|
||||
provider_id: &str,
|
||||
model_id: &str,
|
||||
) -> Result<Option<latest::llm_provider::CacheConfiguration>> {
|
||||
match self {
|
||||
Extension::V0_8_0(ext) => {
|
||||
ext.call_llm_cache_configuration(store, provider_id, model_id)
|
||||
.await
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ToWasmtimeResult<T> {
|
||||
|
||||
@@ -9,7 +9,6 @@ use wasmtime::component::{Linker, Resource};
|
||||
use super::latest;
|
||||
|
||||
pub const MIN_VERSION: Version = Version::new(0, 6, 0);
|
||||
#[allow(dead_code)]
|
||||
pub const MAX_VERSION: Version = Version::new(0, 7, 0);
|
||||
|
||||
wasmtime::component::bindgen!({
|
||||
@@ -33,6 +32,8 @@ wasmtime::component::bindgen!({
|
||||
},
|
||||
});
|
||||
|
||||
pub use self::zed::extension::*;
|
||||
|
||||
mod settings {
|
||||
#![allow(dead_code)]
|
||||
include!(concat!(env!("OUT_DIR"), "/since_v0.6.0/settings.rs"));
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::wasm_host::wit::since_v0_8_0::{
|
||||
use crate::wasm_host::wit::since_v0_6_0::{
|
||||
dap::{
|
||||
AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, LaunchRequest,
|
||||
StartDebuggingRequestArguments, TcpArguments, TcpArgumentsTemplate,
|
||||
},
|
||||
lsp::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind},
|
||||
slash_command::SlashCommandOutputSection,
|
||||
};
|
||||
use crate::wasm_host::wit::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind};
|
||||
use crate::wasm_host::{WasmState, wit::ToWasmtimeResult};
|
||||
use ::http_client::{AsyncBody, HttpRequestExt};
|
||||
use ::settings::{Settings, WorktreeId};
|
||||
@@ -13,7 +13,6 @@ use anyhow::{Context as _, Result, bail};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use credentials_provider::CredentialsProvider;
|
||||
use extension::{
|
||||
ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate,
|
||||
};
|
||||
@@ -23,14 +22,12 @@ use gpui::{BackgroundExecutor, SharedString};
|
||||
use language::{BinaryStatus, LanguageName, language_settings::AllLanguageSettings};
|
||||
use project::project_settings::ProjectSettings;
|
||||
use semver::Version;
|
||||
use smol::net::TcpListener;
|
||||
use std::{
|
||||
env,
|
||||
net::Ipv4Addr,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::{Arc, OnceLock},
|
||||
time::Duration,
|
||||
};
|
||||
use task::{SpawnInTerminal, ZedDebugConfig};
|
||||
use url::Url;
|
||||
@@ -1110,336 +1107,3 @@ impl ExtensionImports for WasmState {
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
}
|
||||
|
||||
impl llm_provider::Host for WasmState {
|
||||
async fn request_credential(
|
||||
&mut self,
|
||||
_provider_id: String,
|
||||
_credential_type: llm_provider::CredentialType,
|
||||
_label: String,
|
||||
_placeholder: String,
|
||||
) -> wasmtime::Result<Result<bool, String>> {
|
||||
// For now, credential requests return false (not provided)
|
||||
// Extensions should use get_env_var to check for env vars first,
|
||||
// then store_credential/get_credential for manual storage
|
||||
// Full UI credential prompting will be added in a future phase
|
||||
Ok(Ok(false))
|
||||
}
|
||||
|
||||
async fn get_credential(&mut self, provider_id: String) -> wasmtime::Result<Option<String>> {
|
||||
let extension_id = self.manifest.id.clone();
|
||||
|
||||
// Check if this provider has an env var configured and if the user has allowed it
|
||||
let env_var_name = self
|
||||
.manifest
|
||||
.language_model_providers
|
||||
.get(&Arc::<str>::from(provider_id.as_str()))
|
||||
.and_then(|entry| entry.auth.as_ref())
|
||||
.and_then(|auth| auth.env_var.clone());
|
||||
|
||||
if let Some(env_var_name) = env_var_name {
|
||||
let full_provider_id: Arc<str> = format!("{}:{}", extension_id, provider_id).into();
|
||||
// Use cached settings from WasmHost instead of going to main thread
|
||||
let is_allowed = self
|
||||
.host
|
||||
.allowed_env_var_providers
|
||||
.contains(&full_provider_id);
|
||||
|
||||
if is_allowed {
|
||||
if let Ok(value) = env::var(&env_var_name) {
|
||||
if !value.is_empty() {
|
||||
return Ok(Some(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to credential store
|
||||
let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
|
||||
|
||||
self.on_main_thread(move |cx| {
|
||||
async move {
|
||||
let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
|
||||
let result = credentials_provider
|
||||
.read_credentials(&credential_key, cx)
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
Ok(result.map(|(_, password)| String::from_utf8_lossy(&password).to_string()))
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn store_credential(
|
||||
&mut self,
|
||||
provider_id: String,
|
||||
value: String,
|
||||
) -> wasmtime::Result<Result<(), String>> {
|
||||
let extension_id = self.manifest.id.clone();
|
||||
let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
|
||||
|
||||
self.on_main_thread(move |cx| {
|
||||
async move {
|
||||
let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
|
||||
credentials_provider
|
||||
.write_credentials(&credential_key, "api_key", value.as_bytes(), cx)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
.await
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
|
||||
async fn delete_credential(
|
||||
&mut self,
|
||||
provider_id: String,
|
||||
) -> wasmtime::Result<Result<(), String>> {
|
||||
let extension_id = self.manifest.id.clone();
|
||||
let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
|
||||
|
||||
self.on_main_thread(move |cx| {
|
||||
async move {
|
||||
let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
|
||||
credentials_provider
|
||||
.delete_credentials(&credential_key, cx)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
.await
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
|
||||
async fn get_env_var(&mut self, name: String) -> wasmtime::Result<Option<String>> {
|
||||
let extension_id = self.manifest.id.clone();
|
||||
|
||||
// Find which provider (if any) declares this env var in its auth config
|
||||
let mut allowed_provider_id: Option<Arc<str>> = None;
|
||||
for (provider_id, provider_entry) in &self.manifest.language_model_providers {
|
||||
if let Some(auth_config) = &provider_entry.auth {
|
||||
if auth_config.env_var.as_deref() == Some(&name) {
|
||||
allowed_provider_id = Some(provider_id.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no provider declares this env var, deny access
|
||||
let Some(provider_id) = allowed_provider_id else {
|
||||
log::warn!(
|
||||
"Extension {} attempted to read env var {} which is not declared in any provider auth config",
|
||||
extension_id,
|
||||
name
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Check if the user has allowed this provider to read env vars
|
||||
// Use cached settings from WasmHost instead of going to main thread
|
||||
let full_provider_id: Arc<str> = format!("{}:{}", extension_id, provider_id).into();
|
||||
let is_allowed = self
|
||||
.host
|
||||
.allowed_env_var_providers
|
||||
.contains(&full_provider_id);
|
||||
|
||||
if !is_allowed {
|
||||
log::debug!(
|
||||
"Extension {} provider {} is not allowed to read env var {}",
|
||||
extension_id,
|
||||
provider_id,
|
||||
name
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(env::var(&name).ok())
|
||||
}
|
||||
|
||||
async fn oauth_start_web_auth(
|
||||
&mut self,
|
||||
config: llm_provider::OauthWebAuthConfig,
|
||||
) -> wasmtime::Result<Result<llm_provider::OauthWebAuthResult, String>> {
|
||||
let auth_url = config.auth_url;
|
||||
let callback_path = config.callback_path;
|
||||
let timeout_secs = config.timeout_secs.unwrap_or(300);
|
||||
|
||||
self.on_main_thread(move |cx| {
|
||||
async move {
|
||||
let listener = TcpListener::bind("127.0.0.1:0")
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to bind localhost server: {}", e))?;
|
||||
let port = listener
|
||||
.local_addr()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to get local address: {}", e))?
|
||||
.port();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.open_url(&auth_url);
|
||||
})?;
|
||||
|
||||
let accept_future = async {
|
||||
let (mut stream, _) = listener
|
||||
.accept()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to accept connection: {}", e))?;
|
||||
|
||||
let mut request_line = String::new();
|
||||
{
|
||||
let mut reader = smol::io::BufReader::new(&mut stream);
|
||||
smol::io::AsyncBufReadExt::read_line(&mut reader, &mut request_line)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to read request: {}", e))?;
|
||||
}
|
||||
|
||||
let callback_url = if let Some(path_start) = request_line.find(' ') {
|
||||
if let Some(path_end) = request_line[path_start + 1..].find(' ') {
|
||||
let path = &request_line[path_start + 1..path_start + 1 + path_end];
|
||||
if path.starts_with(&callback_path) || path.starts_with(&format!("/{}", callback_path.trim_start_matches('/'))) {
|
||||
format!("http://localhost:{}{}", port, path)
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unexpected callback path: {}",
|
||||
path
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Malformed HTTP request"));
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Malformed HTTP request"));
|
||||
};
|
||||
|
||||
let response = "HTTP/1.1 200 OK\r\n\
|
||||
Content-Type: text/html\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
<!DOCTYPE html>\
|
||||
<html><head><title>Authentication Complete</title></head>\
|
||||
<body style=\"font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;\">\
|
||||
<div style=\"text-align: center;\">\
|
||||
<h1>Authentication Complete</h1>\
|
||||
<p>You can close this window and return to Zed.</p>\
|
||||
</div></body></html>";
|
||||
|
||||
smol::io::AsyncWriteExt::write_all(&mut stream, response.as_bytes())
|
||||
.await
|
||||
.ok();
|
||||
smol::io::AsyncWriteExt::flush(&mut stream).await.ok();
|
||||
|
||||
Ok(callback_url)
|
||||
};
|
||||
|
||||
let timeout_duration = Duration::from_secs(timeout_secs as u64);
|
||||
let callback_url = smol::future::or(
|
||||
accept_future,
|
||||
async {
|
||||
smol::Timer::after(timeout_duration).await;
|
||||
Err(anyhow::anyhow!(
|
||||
"OAuth callback timed out after {} seconds",
|
||||
timeout_secs
|
||||
))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(llm_provider::OauthWebAuthResult {
|
||||
callback_url,
|
||||
port: port as u32,
|
||||
})
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
.await
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
|
||||
async fn send_oauth_http_request(
|
||||
&mut self,
|
||||
request: llm_provider::OauthHttpRequest,
|
||||
) -> wasmtime::Result<Result<llm_provider::OauthHttpResponse, String>> {
|
||||
let http_client = self.host.http_client.clone();
|
||||
|
||||
self.on_main_thread(move |_cx| {
|
||||
async move {
|
||||
let method = match request.method.to_uppercase().as_str() {
|
||||
"GET" => ::http_client::Method::GET,
|
||||
"POST" => ::http_client::Method::POST,
|
||||
"PUT" => ::http_client::Method::PUT,
|
||||
"DELETE" => ::http_client::Method::DELETE,
|
||||
"PATCH" => ::http_client::Method::PATCH,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Unsupported HTTP method: {}",
|
||||
request.method
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = ::http_client::Request::builder()
|
||||
.method(method)
|
||||
.uri(&request.url);
|
||||
|
||||
for (key, value) in &request.headers {
|
||||
builder = builder.header(key.as_str(), value.as_str());
|
||||
}
|
||||
|
||||
let body = if request.body.is_empty() {
|
||||
AsyncBody::empty()
|
||||
} else {
|
||||
AsyncBody::from(request.body.into_bytes())
|
||||
};
|
||||
|
||||
let http_request = builder
|
||||
.body(body)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to build request: {}", e))?;
|
||||
|
||||
let mut response = http_client
|
||||
.send(http_request)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("HTTP request failed: {}", e))?;
|
||||
|
||||
let status = response.status().as_u16();
|
||||
let headers: Vec<(String, String)> = response
|
||||
.headers()
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
|
||||
.collect();
|
||||
|
||||
let mut body_bytes = Vec::new();
|
||||
futures::AsyncReadExt::read_to_end(response.body_mut(), &mut body_bytes)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to read response body: {}", e))?;
|
||||
|
||||
let body = String::from_utf8_lossy(&body_bytes).to_string();
|
||||
|
||||
Ok(llm_provider::OauthHttpResponse {
|
||||
status,
|
||||
headers,
|
||||
body,
|
||||
})
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
.await
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
|
||||
async fn oauth_open_browser(&mut self, url: String) -> wasmtime::Result<Result<(), String>> {
|
||||
self.on_main_thread(move |cx| {
|
||||
async move {
|
||||
cx.update(|cx| {
|
||||
cx.open_url(&url);
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
.await
|
||||
.to_wasmtime_result()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,6 @@ pub struct FakeGitRepositoryState {
|
||||
pub blames: HashMap<RepoPath, Blame>,
|
||||
pub current_branch_name: Option<String>,
|
||||
pub branches: HashSet<String>,
|
||||
/// List of remotes, keys are names and values are URLs
|
||||
pub remotes: HashMap<String, String>,
|
||||
pub simulated_index_write_error_message: Option<String>,
|
||||
pub refs: HashMap<String, String>,
|
||||
}
|
||||
@@ -70,7 +68,6 @@ impl FakeGitRepositoryState {
|
||||
refs: HashMap::from_iter([("HEAD".into(), "abc".into())]),
|
||||
merge_base_contents: Default::default(),
|
||||
oids: Default::default(),
|
||||
remotes: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,13 +432,8 @@ impl GitRepository for FakeGitRepository {
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_branch(&self, name: String) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
if !state.branches.remove(&name) {
|
||||
bail!("no such branch: {name}");
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
fn delete_branch(&self, _name: String) -> BoxFuture<'_, Result<()>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn blame(&self, path: RepoPath, _content: Rope) -> BoxFuture<'_, Result<git::blame::Blame>> {
|
||||
@@ -606,19 +598,6 @@ impl GitRepository for FakeGitRepository {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_all_remotes(&self) -> BoxFuture<'_, Result<Vec<Remote>>> {
|
||||
self.with_state_async(false, move |state| {
|
||||
let remotes = state
|
||||
.remotes
|
||||
.keys()
|
||||
.map(|r| Remote {
|
||||
name: r.clone().into(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Ok(remotes)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_push_remote(&self, _branch: String) -> BoxFuture<'_, Result<Option<Remote>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -627,6 +606,10 @@ impl GitRepository for FakeGitRepository {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_all_remotes(&self) -> BoxFuture<'_, Result<Vec<Remote>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn check_for_pushed_commit(&self) -> BoxFuture<'_, Result<Vec<gpui::SharedString>>> {
|
||||
future::ready(Ok(Vec::new())).boxed()
|
||||
}
|
||||
@@ -700,20 +683,6 @@ impl GitRepository for FakeGitRepository {
|
||||
fn default_branch(&self) -> BoxFuture<'_, Result<Option<SharedString>>> {
|
||||
async { Ok(Some("main".into())) }.boxed()
|
||||
}
|
||||
|
||||
fn create_remote(&self, name: String, url: String) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
state.remotes.insert(name, url);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_remote(&self, name: String) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
state.remotes.remove(&name);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use derive_more::Deref;
|
||||
@@ -12,7 +11,7 @@ pub struct RemoteUrl(Url);
|
||||
static USERNAME_REGEX: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"^[0-9a-zA-Z\-_]+@").expect("Failed to create USERNAME_REGEX"));
|
||||
|
||||
impl FromStr for RemoteUrl {
|
||||
impl std::str::FromStr for RemoteUrl {
|
||||
type Err = url::ParseError;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
@@ -7,15 +7,13 @@ use collections::HashMap;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::io::BufWriter;
|
||||
use futures::{AsyncWriteExt, FutureExt as _, select_biased};
|
||||
use git2::{BranchType, ErrorCode};
|
||||
use git2::BranchType;
|
||||
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task};
|
||||
use parking_lot::Mutex;
|
||||
use rope::Rope;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use smol::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::process::{ExitStatus, Stdio};
|
||||
use std::{
|
||||
@@ -57,12 +55,6 @@ impl Branch {
|
||||
self.ref_name.starts_with("refs/remotes/")
|
||||
}
|
||||
|
||||
pub fn remote_name(&self) -> Option<&str> {
|
||||
self.ref_name
|
||||
.strip_prefix("refs/remotes/")
|
||||
.and_then(|stripped| stripped.split("/").next())
|
||||
}
|
||||
|
||||
pub fn tracking_status(&self) -> Option<UpstreamTrackingStatus> {
|
||||
self.upstream
|
||||
.as_ref()
|
||||
@@ -598,10 +590,6 @@ pub trait GitRepository: Send + Sync {
|
||||
|
||||
fn get_all_remotes(&self) -> BoxFuture<'_, Result<Vec<Remote>>>;
|
||||
|
||||
fn remove_remote(&self, name: String) -> BoxFuture<'_, Result<()>>;
|
||||
|
||||
fn create_remote(&self, name: String, url: String) -> BoxFuture<'_, Result<()>>;
|
||||
|
||||
/// returns a list of remote branches that contain HEAD
|
||||
fn check_for_pushed_commit(&self) -> BoxFuture<'_, Result<Vec<SharedString>>>;
|
||||
|
||||
@@ -1397,19 +1385,9 @@ impl GitRepository for RealGitRepository {
|
||||
branch
|
||||
} else if let Ok(revision) = repo.find_branch(&name, BranchType::Remote) {
|
||||
let (_, branch_name) = name.split_once("/").context("Unexpected branch format")?;
|
||||
|
||||
let revision = revision.get();
|
||||
let branch_commit = revision.peel_to_commit()?;
|
||||
let mut branch = match repo.branch(&branch_name, &branch_commit, false) {
|
||||
Ok(branch) => branch,
|
||||
Err(err) if err.code() == ErrorCode::Exists => {
|
||||
repo.find_branch(&branch_name, BranchType::Local)?
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
let mut branch = repo.branch(&branch_name, &branch_commit, false)?;
|
||||
branch.set_upstream(Some(&name))?;
|
||||
branch
|
||||
} else {
|
||||
@@ -1425,6 +1403,7 @@ impl GitRepository for RealGitRepository {
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let branch = branch.await?;
|
||||
|
||||
GitBinary::new(git_binary_path, working_directory?, executor)
|
||||
.run(&["checkout", &branch])
|
||||
.await?;
|
||||
@@ -2014,7 +1993,7 @@ impl GitRepository for RealGitRepository {
|
||||
let working_directory = working_directory?;
|
||||
let output = new_smol_command(&git_binary_path)
|
||||
.current_dir(&working_directory)
|
||||
.args(["remote", "-v"])
|
||||
.args(["remote"])
|
||||
.output()
|
||||
.await?;
|
||||
|
||||
@@ -2023,43 +2002,14 @@ impl GitRepository for RealGitRepository {
|
||||
"Failed to get all remotes:\n{}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
let remote_names: HashSet<Remote> = String::from_utf8_lossy(&output.stdout)
|
||||
.lines()
|
||||
.filter(|line| !line.is_empty())
|
||||
.filter_map(|line| {
|
||||
let mut split_line = line.split_whitespace();
|
||||
let remote_name = split_line.next()?;
|
||||
|
||||
Some(Remote {
|
||||
name: remote_name.trim().to_string().into(),
|
||||
})
|
||||
let remote_names = String::from_utf8_lossy(&output.stdout)
|
||||
.split('\n')
|
||||
.filter(|name| !name.is_empty())
|
||||
.map(|name| Remote {
|
||||
name: name.trim().to_string().into(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(remote_names.into_iter().collect())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn remove_remote(&self, name: String) -> BoxFuture<'_, Result<()>> {
|
||||
let repo = self.repository.clone();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let repo = repo.lock();
|
||||
repo.remote_delete(&name)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn create_remote(&self, name: String, url: String) -> BoxFuture<'_, Result<()>> {
|
||||
let repo = self.repository.clone();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let repo = repo.lock();
|
||||
repo.remote(&name, url.as_ref())?;
|
||||
Ok(())
|
||||
Ok(remote_names)
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3463,6 +3463,7 @@ impl GitPanel {
|
||||
) -> Option<impl IntoElement> {
|
||||
let active_repository = self.active_repository.clone()?;
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
|
||||
let enable_coauthors = self.render_co_authors(cx);
|
||||
|
||||
let editor_focus_handle = self.commit_editor.focus_handle(cx);
|
||||
@@ -4771,6 +4772,7 @@ impl RenderOnce for PanelRepoFooter {
|
||||
const MAX_REPO_LEN: usize = 16;
|
||||
const LABEL_CHARACTER_BUDGET: usize = MAX_BRANCH_LEN + MAX_REPO_LEN;
|
||||
const MAX_SHORT_SHA_LEN: usize = 8;
|
||||
|
||||
let branch_name = self
|
||||
.branch
|
||||
.as_ref()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use anyhow::Context as _;
|
||||
|
||||
use git::repository::{Remote, RemoteCommandOutput};
|
||||
use linkify::{LinkFinder, LinkKind};
|
||||
use ui::SharedString;
|
||||
|
||||
@@ -26,13 +26,12 @@ pub(crate) struct LinuxDispatcher {
|
||||
main_thread_id: thread::ThreadId,
|
||||
}
|
||||
|
||||
const MIN_THREADS: usize = 2;
|
||||
|
||||
impl LinuxDispatcher {
|
||||
pub fn new(main_sender: Sender<RunnableVariant>) -> Self {
|
||||
let (background_sender, background_receiver) = flume::unbounded::<RunnableVariant>();
|
||||
let thread_count =
|
||||
std::thread::available_parallelism().map_or(MIN_THREADS, |i| i.get().max(MIN_THREADS));
|
||||
let thread_count = std::thread::available_parallelism()
|
||||
.map(|i| i.get())
|
||||
.unwrap_or(1);
|
||||
|
||||
let mut background_threads = (0..thread_count)
|
||||
.map(|i| {
|
||||
|
||||
@@ -1419,7 +1419,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
|
||||
state.repeat.current_keycode = Some(keycode);
|
||||
|
||||
let rate = state.repeat.characters_per_second;
|
||||
let repeat_interval = Duration::from_secs(1) / rate.max(1);
|
||||
let repeat_interval = Duration::from_secs(1) / rate;
|
||||
let id = state.repeat.current_id;
|
||||
state
|
||||
.loop_handle
|
||||
|
||||
@@ -7,7 +7,9 @@ use std::{
|
||||
use flume::Sender;
|
||||
use util::ResultExt;
|
||||
use windows::{
|
||||
System::Threading::{ThreadPool, ThreadPoolTimer, TimerElapsedHandler, WorkItemHandler},
|
||||
System::Threading::{
|
||||
ThreadPool, ThreadPoolTimer, TimerElapsedHandler, WorkItemHandler, WorkItemPriority,
|
||||
},
|
||||
Win32::{
|
||||
Foundation::{LPARAM, WPARAM},
|
||||
UI::WindowsAndMessaging::PostMessageW,
|
||||
@@ -53,7 +55,7 @@ impl WindowsDispatcher {
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
ThreadPool::RunAsync(&handler).log_err();
|
||||
ThreadPool::RunWithPriorityAsync(&handler, WorkItemPriority::High).log_err();
|
||||
}
|
||||
|
||||
fn dispatch_on_threadpool_after(&self, runnable: RunnableVariant, duration: Duration) {
|
||||
|
||||
@@ -9,6 +9,7 @@ use strum::{EnumIter, EnumString, IntoStaticStr};
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum IconName {
|
||||
Ai,
|
||||
AiAnthropic,
|
||||
AiBedrock,
|
||||
AiClaude,
|
||||
AiDeepSeek,
|
||||
|
||||
@@ -746,11 +746,6 @@ pub trait LanguageModelProvider: 'static {
|
||||
fn icon(&self) -> IconName {
|
||||
IconName::ZedAssistant
|
||||
}
|
||||
/// Returns the path to an external SVG icon for this provider, if any.
|
||||
/// When present, this takes precedence over `icon()`.
|
||||
fn icon_path(&self) -> Option<SharedString> {
|
||||
None
|
||||
}
|
||||
fn default_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>>;
|
||||
fn default_fast_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>>;
|
||||
fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>>;
|
||||
|
||||
@@ -28,7 +28,6 @@ convert_case.workspace = true
|
||||
copilot.workspace = true
|
||||
credentials_provider.workspace = true
|
||||
deepseek = { workspace = true, features = ["schemars"] }
|
||||
extension.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
google_ai = { workspace = true, features = ["schemars"] }
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
use extension::{ExtensionLanguageModelProviderProxy, LanguageModelProviderRegistration};
|
||||
use gpui::{App, Entity};
|
||||
use language_model::{LanguageModelProviderId, LanguageModelRegistry};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Proxy implementation that registers extension-based language model providers
|
||||
/// with the LanguageModelRegistry.
|
||||
pub struct ExtensionLanguageModelProxy {
|
||||
registry: Entity<LanguageModelRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionLanguageModelProxy {
|
||||
pub fn new(registry: Entity<LanguageModelRegistry>) -> Self {
|
||||
Self { registry }
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtensionLanguageModelProviderProxy for ExtensionLanguageModelProxy {
|
||||
fn register_language_model_provider(
|
||||
&self,
|
||||
provider_id: Arc<str>,
|
||||
register_fn: LanguageModelProviderRegistration,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let _ = provider_id;
|
||||
register_fn(cx);
|
||||
}
|
||||
|
||||
fn unregister_language_model_provider(&self, provider_id: Arc<str>, cx: &mut App) {
|
||||
self.registry.update(cx, |registry, cx| {
|
||||
registry.unregister_provider(LanguageModelProviderId::from(provider_id), cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ::extension::ExtensionHostProxy;
|
||||
use ::settings::{Settings, SettingsStore};
|
||||
use client::{Client, UserStore};
|
||||
use collections::HashSet;
|
||||
@@ -9,11 +8,11 @@ use language_model::{LanguageModelProviderId, LanguageModelRegistry};
|
||||
use provider::deepseek::DeepSeekLanguageModelProvider;
|
||||
|
||||
mod api_key;
|
||||
mod extension;
|
||||
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;
|
||||
@@ -34,12 +33,6 @@ pub fn init(user_store: Entity<UserStore>, client: Arc<Client>, cx: &mut App) {
|
||||
register_language_model_providers(registry, user_store, client.clone(), cx);
|
||||
});
|
||||
|
||||
// Register the extension language model provider proxy
|
||||
let extension_proxy = ExtensionHostProxy::default_global(cx);
|
||||
extension_proxy.register_language_model_provider_proxy(
|
||||
extension::ExtensionLanguageModelProxy::new(registry.clone()),
|
||||
);
|
||||
|
||||
let mut openai_compatible_providers = AllLanguageModelSettings::get_global(cx)
|
||||
.openai_compatible
|
||||
.keys()
|
||||
@@ -118,6 +111,13 @@ 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,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod anthropic;
|
||||
pub mod bedrock;
|
||||
pub mod cloud;
|
||||
pub mod copilot_chat;
|
||||
|
||||
1045
crates/language_models/src/provider/anthropic.rs
Normal file
1045
crates/language_models/src/provider/anthropic.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -71,7 +71,6 @@ pub struct AmazonBedrockSettings {
|
||||
pub profile_name: Option<String>,
|
||||
pub role_arn: Option<String>,
|
||||
pub authentication_method: Option<BedrockAuthMethod>,
|
||||
pub allow_global: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, EnumIter, IntoStaticStr, JsonSchema)]
|
||||
@@ -240,13 +239,6 @@ impl State {
|
||||
.or(settings_region)
|
||||
.unwrap_or(String::from("us-east-1"))
|
||||
}
|
||||
|
||||
fn get_allow_global(&self) -> bool {
|
||||
self.settings
|
||||
.as_ref()
|
||||
.and_then(|s| s.allow_global)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BedrockLanguageModelProvider {
|
||||
@@ -553,13 +545,11 @@ impl LanguageModel for BedrockModel {
|
||||
LanguageModelCompletionError,
|
||||
>,
|
||||
> {
|
||||
let Ok((region, allow_global)) = cx.read_entity(&self.state, |state, _cx| {
|
||||
(state.get_region(), state.get_allow_global())
|
||||
}) else {
|
||||
let Ok(region) = cx.read_entity(&self.state, |state, _cx| state.get_region()) else {
|
||||
return async move { Err(anyhow::anyhow!("App State Dropped").into()) }.boxed();
|
||||
};
|
||||
|
||||
let model_id = match self.model.cross_region_inference_id(®ion, allow_global) {
|
||||
let model_id = match self.model.cross_region_inference_id(®ion) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return async move { Err(e.into()) }.boxed();
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use ai_onboarding::YoungAccountBanner;
|
||||
use anthropic::{
|
||||
AnthropicModelMode, ContentDelta, Event, ResponseContent, ToolResultContent, ToolResultPart,
|
||||
Usage,
|
||||
};
|
||||
use anthropic::AnthropicModelMode;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use chrono::{DateTime, Utc};
|
||||
use client::{Client, ModelRequestUsage, UserStore, zed_urls};
|
||||
@@ -26,9 +23,8 @@ use language_model::{
|
||||
LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelId, LanguageModelName,
|
||||
LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName,
|
||||
LanguageModelProviderState, LanguageModelRequest, LanguageModelToolChoice,
|
||||
LanguageModelToolResultContent, LanguageModelToolSchemaFormat, LanguageModelToolUse,
|
||||
LanguageModelToolUseId, LlmApiToken, MessageContent, ModelRequestLimitReachedError,
|
||||
PaymentRequiredError, RateLimiter, RefreshLlmTokenListener, Role, StopReason,
|
||||
LanguageModelToolSchemaFormat, LlmApiToken, ModelRequestLimitReachedError,
|
||||
PaymentRequiredError, RateLimiter, RefreshLlmTokenListener,
|
||||
};
|
||||
use release_channel::AppVersion;
|
||||
use schemars::JsonSchema;
|
||||
@@ -46,6 +42,7 @@ 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;
|
||||
@@ -1397,434 +1394,3 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn count_anthropic_tokens(
|
||||
request: LanguageModelRequest,
|
||||
cx: &App,
|
||||
) -> BoxFuture<'static, Result<u64>> {
|
||||
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<anthropic::Message> = 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<anthropic::RequestContent> = 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<usize, RawToolUse>,
|
||||
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<Result<LanguageModelCompletionEvent, LanguageModelCompletionError>> {
|
||||
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::<serde_json::Value>(&tool_use.input_json)
|
||||
.ok()
|
||||
.and_then(|input| {
|
||||
let input_json_roundtripped = serde_json::to_string(&input).ok()?;
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ use collections::HashMap;
|
||||
use settings::RegisterSetting;
|
||||
|
||||
use crate::provider::{
|
||||
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,
|
||||
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,
|
||||
};
|
||||
|
||||
#[derive(Debug, RegisterSetting)]
|
||||
pub struct AllLanguageModelSettings {
|
||||
pub anthropic: AnthropicSettings,
|
||||
pub bedrock: AmazonBedrockSettings,
|
||||
pub deepseek: DeepSeekSettings,
|
||||
pub google: GoogleSettings,
|
||||
@@ -31,6 +33,7 @@ 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();
|
||||
@@ -44,6 +47,10 @@ 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,
|
||||
@@ -51,7 +58,6 @@ impl settings::Settings for AllLanguageModelSettings {
|
||||
profile_name: bedrock.profile,
|
||||
role_arn: None, // todo(was never a setting for this...)
|
||||
authentication_method: bedrock.authentication_method.map(Into::into),
|
||||
allow_global: bedrock.allow_global,
|
||||
},
|
||||
deepseek: DeepSeekSettings {
|
||||
api_url: deepseek.api_url.unwrap(),
|
||||
|
||||
@@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use settings::Settings;
|
||||
use smol::lock::OnceCell;
|
||||
use std::cmp::{Ordering, Reverse};
|
||||
use std::cmp::Ordering;
|
||||
use std::env::consts;
|
||||
use terminal::terminal_settings::TerminalSettings;
|
||||
use util::command::new_smol_command;
|
||||
@@ -1101,33 +1101,13 @@ fn get_venv_parent_dir(env: &PythonEnvironment) -> Option<PathBuf> {
|
||||
venv.parent().map(|parent| parent.to_path_buf())
|
||||
}
|
||||
|
||||
// How far is this venv from the root of our current project?
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum SubprojectDistance {
|
||||
WithinSubproject(Reverse<usize>),
|
||||
WithinWorktree(Reverse<usize>),
|
||||
NotInWorktree,
|
||||
}
|
||||
|
||||
fn wr_distance(
|
||||
wr: &PathBuf,
|
||||
subroot_relative_path: &RelPath,
|
||||
venv: Option<&PathBuf>,
|
||||
) -> SubprojectDistance {
|
||||
fn wr_distance(wr: &PathBuf, venv: Option<&PathBuf>) -> usize {
|
||||
if let Some(venv) = venv
|
||||
&& let Ok(p) = venv.strip_prefix(wr)
|
||||
{
|
||||
if subroot_relative_path.components().next().is_some()
|
||||
&& let Ok(distance) = p
|
||||
.strip_prefix(subroot_relative_path.as_std_path())
|
||||
.map(|p| p.components().count())
|
||||
{
|
||||
SubprojectDistance::WithinSubproject(Reverse(distance))
|
||||
} else {
|
||||
SubprojectDistance::WithinWorktree(Reverse(p.components().count()))
|
||||
}
|
||||
p.components().count()
|
||||
} else {
|
||||
SubprojectDistance::NotInWorktree
|
||||
usize::MAX
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1190,14 +1170,11 @@ impl ToolchainLister for PythonToolchainProvider {
|
||||
});
|
||||
|
||||
// Compare project paths against worktree root
|
||||
let proj_ordering =
|
||||
|| {
|
||||
let lhs_project = lhs.project.clone().or_else(|| get_venv_parent_dir(lhs));
|
||||
let rhs_project = rhs.project.clone().or_else(|| get_venv_parent_dir(rhs));
|
||||
wr_distance(&wr, &subroot_relative_path, lhs_project.as_ref()).cmp(
|
||||
&wr_distance(&wr, &subroot_relative_path, rhs_project.as_ref()),
|
||||
)
|
||||
};
|
||||
let proj_ordering = || {
|
||||
let lhs_project = lhs.project.clone().or_else(|| get_venv_parent_dir(lhs));
|
||||
let rhs_project = rhs.project.clone().or_else(|| get_venv_parent_dir(rhs));
|
||||
wr_distance(&wr, lhs_project.as_ref()).cmp(&wr_distance(&wr, rhs_project.as_ref()))
|
||||
};
|
||||
|
||||
// Compare environment priorities
|
||||
let priority_ordering = || env_priority(lhs.kind).cmp(&env_priority(rhs.kind));
|
||||
|
||||
@@ -43,7 +43,7 @@ use std::{
|
||||
io,
|
||||
iter::{self, FromIterator},
|
||||
mem,
|
||||
ops::{self, AddAssign, ControlFlow, Range, RangeBounds, Sub, SubAssign},
|
||||
ops::{self, AddAssign, Range, RangeBounds, Sub, SubAssign},
|
||||
rc::Rc,
|
||||
str,
|
||||
sync::Arc,
|
||||
@@ -4618,24 +4618,7 @@ impl MultiBufferSnapshot {
|
||||
cx: &App,
|
||||
) -> BTreeMap<MultiBufferRow, IndentSize> {
|
||||
let mut result = BTreeMap::new();
|
||||
self.suggested_indents_callback(
|
||||
rows,
|
||||
|row, indent| {
|
||||
result.insert(row, indent);
|
||||
ControlFlow::Continue(())
|
||||
},
|
||||
cx,
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
// move this to be a generator once those are a thing
|
||||
pub fn suggested_indents_callback(
|
||||
&self,
|
||||
rows: impl IntoIterator<Item = u32>,
|
||||
mut cb: impl FnMut(MultiBufferRow, IndentSize) -> ControlFlow<()>,
|
||||
cx: &App,
|
||||
) {
|
||||
let mut rows_for_excerpt = Vec::new();
|
||||
let mut cursor = self.cursor::<Point, Point>();
|
||||
let mut rows = rows.into_iter().peekable();
|
||||
@@ -4679,17 +4662,16 @@ impl MultiBufferSnapshot {
|
||||
let buffer_indents = region
|
||||
.buffer
|
||||
.suggested_indents(buffer_rows, single_indent_size);
|
||||
for (row, indent) in buffer_indents {
|
||||
if cb(
|
||||
let multibuffer_indents = buffer_indents.into_iter().map(|(row, indent)| {
|
||||
(
|
||||
MultiBufferRow(start_multibuffer_row + row - start_buffer_row),
|
||||
indent,
|
||||
)
|
||||
.is_break()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
result.extend(multibuffer_indents);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn indent_size_for_line(&self, row: MultiBufferRow) -> IndentSize {
|
||||
|
||||
@@ -2,8 +2,7 @@ use anyhow::Context as _;
|
||||
use collections::{HashMap, HashSet};
|
||||
use fs::Fs;
|
||||
use gpui::{AsyncApp, Entity};
|
||||
use language::language_settings::PrettierSettings;
|
||||
use language::{Buffer, Diff, Language, language_settings::language_settings};
|
||||
use language::{Buffer, Diff, language_settings::language_settings};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use node_runtime::NodeRuntime;
|
||||
use paths::default_prettier_dir;
|
||||
@@ -350,7 +349,7 @@ impl Prettier {
|
||||
Self::Real(local) => {
|
||||
let params = buffer
|
||||
.update(cx, |buffer, cx| {
|
||||
let buffer_language = buffer.language().map(|language| language.as_ref());
|
||||
let buffer_language = buffer.language();
|
||||
let language_settings = language_settings(buffer_language.map(|l| l.name()), buffer.file(), cx);
|
||||
let prettier_settings = &language_settings.prettier;
|
||||
anyhow::ensure!(
|
||||
@@ -450,7 +449,15 @@ impl Prettier {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let parser = prettier_parser_name(buffer_path.as_deref(), buffer_language, prettier_settings).context("getting prettier parser")?;
|
||||
let mut prettier_parser = prettier_settings.parser.as_deref();
|
||||
if buffer_path.is_none() {
|
||||
prettier_parser = prettier_parser.or_else(|| buffer_language.and_then(|language| language.prettier_parser_name()));
|
||||
if prettier_parser.is_none() {
|
||||
log::error!("Formatting unsaved file with prettier failed. No prettier parser configured for language {buffer_language:?}");
|
||||
anyhow::bail!("Cannot determine prettier parser for unsaved file");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let ignore_path = ignore_dir.and_then(|dir| {
|
||||
let ignore_file = dir.join(".prettierignore");
|
||||
@@ -468,15 +475,15 @@ impl Prettier {
|
||||
anyhow::Ok(FormatParams {
|
||||
text: buffer.text(),
|
||||
options: FormatOptions {
|
||||
path: buffer_path,
|
||||
parser,
|
||||
parser: prettier_parser.map(ToOwned::to_owned),
|
||||
plugins,
|
||||
path: buffer_path,
|
||||
prettier_options,
|
||||
ignore_path,
|
||||
},
|
||||
})
|
||||
})?
|
||||
.context("building prettier request")?;
|
||||
})?
|
||||
.context("building prettier request")?;
|
||||
|
||||
let response = local
|
||||
.server
|
||||
@@ -496,26 +503,7 @@ impl Prettier {
|
||||
{
|
||||
Some("rust") => anyhow::bail!("prettier does not support Rust"),
|
||||
Some(_other) => {
|
||||
let mut formatted_text = buffer.text() + FORMAT_SUFFIX;
|
||||
|
||||
let buffer_language =
|
||||
buffer.language().map(|language| language.as_ref());
|
||||
let language_settings = language_settings(
|
||||
buffer_language.map(|l| l.name()),
|
||||
buffer.file(),
|
||||
cx,
|
||||
);
|
||||
let prettier_settings = &language_settings.prettier;
|
||||
let parser = prettier_parser_name(
|
||||
buffer_path.as_deref(),
|
||||
buffer_language,
|
||||
prettier_settings,
|
||||
)?;
|
||||
|
||||
if let Some(parser) = parser {
|
||||
formatted_text = format!("{formatted_text}\n{parser}");
|
||||
}
|
||||
|
||||
let formatted_text = buffer.text() + FORMAT_SUFFIX;
|
||||
Ok(buffer.diff(formatted_text, cx))
|
||||
}
|
||||
None => panic!("Should not format buffer without a language with prettier"),
|
||||
@@ -563,40 +551,6 @@ impl Prettier {
|
||||
}
|
||||
}
|
||||
|
||||
fn prettier_parser_name(
|
||||
buffer_path: Option<&Path>,
|
||||
buffer_language: Option<&Language>,
|
||||
prettier_settings: &PrettierSettings,
|
||||
) -> anyhow::Result<Option<String>> {
|
||||
let parser = if buffer_path.is_none() {
|
||||
let parser = prettier_settings
|
||||
.parser
|
||||
.as_deref()
|
||||
.or_else(|| buffer_language.and_then(|language| language.prettier_parser_name()));
|
||||
if parser.is_none() {
|
||||
log::error!(
|
||||
"Formatting unsaved file with prettier failed. No prettier parser configured for language {buffer_language:?}"
|
||||
);
|
||||
anyhow::bail!("Cannot determine prettier parser for unsaved file");
|
||||
}
|
||||
parser
|
||||
} else if let (Some(buffer_language), Some(buffer_path)) = (buffer_language, buffer_path)
|
||||
&& buffer_path.extension().is_some_and(|extension| {
|
||||
!buffer_language
|
||||
.config()
|
||||
.matcher
|
||||
.path_suffixes
|
||||
.contains(&extension.to_string_lossy().into_owned())
|
||||
})
|
||||
{
|
||||
buffer_language.prettier_parser_name()
|
||||
} else {
|
||||
prettier_settings.parser.as_deref()
|
||||
};
|
||||
|
||||
Ok(parser.map(ToOwned::to_owned))
|
||||
}
|
||||
|
||||
async fn has_prettier_in_node_modules(fs: &dyn Fs, path: &Path) -> anyhow::Result<bool> {
|
||||
let possible_node_modules_location = path.join("node_modules").join(PRETTIER_PACKAGE_NAME);
|
||||
if let Some(node_modules_location_metadata) = fs
|
||||
|
||||
@@ -453,9 +453,7 @@ impl AgentServerStore {
|
||||
.clone()
|
||||
.and_then(|settings| settings.custom_command()),
|
||||
http_client: http_client.clone(),
|
||||
no_browser: downstream_client
|
||||
.as_ref()
|
||||
.is_some_and(|(_, client)| !client.has_wsl_interop()),
|
||||
is_remote: downstream_client.is_some(),
|
||||
}),
|
||||
);
|
||||
self.external_agents.insert(
|
||||
@@ -1357,7 +1355,7 @@ struct LocalCodex {
|
||||
project_environment: Entity<ProjectEnvironment>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
custom_command: Option<AgentServerCommand>,
|
||||
no_browser: bool,
|
||||
is_remote: bool,
|
||||
}
|
||||
|
||||
impl ExternalAgentServer for LocalCodex {
|
||||
@@ -1377,7 +1375,7 @@ impl ExternalAgentServer for LocalCodex {
|
||||
.map(|root_dir| Path::new(root_dir))
|
||||
.unwrap_or(paths::home_dir())
|
||||
.into();
|
||||
let no_browser = self.no_browser;
|
||||
let is_remote = self.is_remote;
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let mut env = project_environment
|
||||
@@ -1390,7 +1388,7 @@ impl ExternalAgentServer for LocalCodex {
|
||||
})?
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
if no_browser {
|
||||
if is_remote {
|
||||
env.insert("NO_BROWSER".to_owned(), "1".to_owned());
|
||||
}
|
||||
|
||||
|
||||
@@ -472,8 +472,6 @@ impl GitStore {
|
||||
client.add_entity_request_handler(Self::handle_change_branch);
|
||||
client.add_entity_request_handler(Self::handle_create_branch);
|
||||
client.add_entity_request_handler(Self::handle_rename_branch);
|
||||
client.add_entity_request_handler(Self::handle_create_remote);
|
||||
client.add_entity_request_handler(Self::handle_remove_remote);
|
||||
client.add_entity_request_handler(Self::handle_delete_branch);
|
||||
client.add_entity_request_handler(Self::handle_git_init);
|
||||
client.add_entity_request_handler(Self::handle_push);
|
||||
@@ -2276,25 +2274,6 @@ impl GitStore {
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_create_remote(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitCreateRemote>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
let remote_name = envelope.payload.remote_name;
|
||||
let remote_url = envelope.payload.remote_url;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.create_remote(remote_name, remote_url)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_delete_branch(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitDeleteBranch>,
|
||||
@@ -2313,24 +2292,6 @@ impl GitStore {
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_remove_remote(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitRemoveRemote>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
let remote_name = envelope.payload.remote_name;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.remove_remote(remote_name)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_show(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitShow>,
|
||||
@@ -4904,61 +4865,6 @@ impl Repository {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_remote(
|
||||
&mut self,
|
||||
remote_name: String,
|
||||
remote_url: String,
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(
|
||||
Some(format!("git remote add {remote_name} {remote_url}").into()),
|
||||
move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.create_remote(remote_name, remote_url).await
|
||||
}
|
||||
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
|
||||
client
|
||||
.request(proto::GitCreateRemote {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
remote_name,
|
||||
remote_url,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn remove_remote(&mut self, remote_name: String) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(
|
||||
Some(format!("git remove remote {remote_name}").into()),
|
||||
move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.remove_remote(remote_name).await
|
||||
}
|
||||
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
|
||||
client
|
||||
.request(proto::GitRemoveRemote {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
remote_name,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_remotes(
|
||||
&mut self,
|
||||
branch_name: Option<String>,
|
||||
@@ -4996,7 +4902,7 @@ impl Repository {
|
||||
let remotes = response
|
||||
.remotes
|
||||
.into_iter()
|
||||
.map(|remotes| Remote {
|
||||
.map(|remotes| git::repository::Remote {
|
||||
name: remotes.name.into(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -93,6 +93,9 @@ enum FindSearchCandidates {
|
||||
/// based on disk contents of a buffer. This step is not performed for buffers we already have in memory.
|
||||
confirm_contents_will_match_tx: Sender<MatchingEntry>,
|
||||
confirm_contents_will_match_rx: Receiver<MatchingEntry>,
|
||||
/// Of those that contain at least one match (or are already in memory), look for rest of matches (and figure out their ranges).
|
||||
/// But wait - first, we need to go back to the main thread to open a buffer (& create an entity for it).
|
||||
get_buffer_for_full_scan_tx: Sender<ProjectPath>,
|
||||
},
|
||||
Remote,
|
||||
OpenBuffersOnly,
|
||||
@@ -223,7 +226,7 @@ impl Search {
|
||||
.boxed_local(),
|
||||
cx.background_spawn(Self::maintain_sorted_search_results(
|
||||
sorted_search_results_rx,
|
||||
get_buffer_for_full_scan_tx,
|
||||
get_buffer_for_full_scan_tx.clone(),
|
||||
self.limit,
|
||||
))
|
||||
.boxed_local(),
|
||||
@@ -231,6 +234,7 @@ impl Search {
|
||||
(
|
||||
FindSearchCandidates::Local {
|
||||
fs,
|
||||
get_buffer_for_full_scan_tx,
|
||||
confirm_contents_will_match_tx,
|
||||
confirm_contents_will_match_rx,
|
||||
input_paths_rx,
|
||||
@@ -589,6 +593,7 @@ impl Worker<'_> {
|
||||
input_paths_rx,
|
||||
confirm_contents_will_match_rx,
|
||||
mut confirm_contents_will_match_tx,
|
||||
mut get_buffer_for_full_scan_tx,
|
||||
fs,
|
||||
) = match self.candidates {
|
||||
FindSearchCandidates::Local {
|
||||
@@ -596,15 +601,21 @@ impl Worker<'_> {
|
||||
input_paths_rx,
|
||||
confirm_contents_will_match_rx,
|
||||
confirm_contents_will_match_tx,
|
||||
get_buffer_for_full_scan_tx,
|
||||
} => (
|
||||
input_paths_rx,
|
||||
confirm_contents_will_match_rx,
|
||||
confirm_contents_will_match_tx,
|
||||
get_buffer_for_full_scan_tx,
|
||||
Some(fs),
|
||||
),
|
||||
FindSearchCandidates::Remote | FindSearchCandidates::OpenBuffersOnly => {
|
||||
(unbounded().1, unbounded().1, unbounded().0, None)
|
||||
}
|
||||
FindSearchCandidates::Remote | FindSearchCandidates::OpenBuffersOnly => (
|
||||
unbounded().1,
|
||||
unbounded().1,
|
||||
unbounded().0,
|
||||
unbounded().0,
|
||||
None,
|
||||
),
|
||||
};
|
||||
// WorkerA: grabs a request for "find all matches in file/a" <- takes 5 minutes
|
||||
// right after: WorkerB: grabs a request for "find all matches in file/b" <- takes 5 seconds
|
||||
@@ -618,6 +629,7 @@ impl Worker<'_> {
|
||||
open_entries: &self.open_buffers,
|
||||
fs: fs.as_deref(),
|
||||
confirm_contents_will_match_tx: &confirm_contents_will_match_tx,
|
||||
get_buffer_for_full_scan_tx: &get_buffer_for_full_scan_tx,
|
||||
};
|
||||
// Whenever we notice that some step of a pipeline is closed, we don't want to close subsequent
|
||||
// steps straight away. Another worker might be about to produce a value that will
|
||||
@@ -633,7 +645,10 @@ impl Worker<'_> {
|
||||
find_first_match = find_first_match.next() => {
|
||||
if let Some(buffer_with_at_least_one_match) = find_first_match {
|
||||
handler.handle_find_first_match(buffer_with_at_least_one_match).await;
|
||||
} else {
|
||||
get_buffer_for_full_scan_tx = bounded(1).0;
|
||||
}
|
||||
|
||||
},
|
||||
scan_path = scan_path.next() => {
|
||||
if let Some(path_to_scan) = scan_path {
|
||||
@@ -658,6 +673,7 @@ struct RequestHandler<'worker> {
|
||||
fs: Option<&'worker dyn Fs>,
|
||||
open_entries: &'worker HashSet<ProjectEntryId>,
|
||||
confirm_contents_will_match_tx: &'worker Sender<MatchingEntry>,
|
||||
get_buffer_for_full_scan_tx: &'worker Sender<ProjectPath>,
|
||||
}
|
||||
|
||||
impl RequestHandler<'_> {
|
||||
@@ -713,8 +729,9 @@ impl RequestHandler<'_> {
|
||||
_ = maybe!(async move {
|
||||
let InputPath {
|
||||
entry,
|
||||
|
||||
snapshot,
|
||||
mut should_scan_tx,
|
||||
should_scan_tx,
|
||||
} = req;
|
||||
|
||||
if entry.is_fifo || !entry.is_file() {
|
||||
@@ -737,7 +754,7 @@ impl RequestHandler<'_> {
|
||||
if self.open_entries.contains(&entry.id) {
|
||||
// The buffer is already in memory and that's the version we want to scan;
|
||||
// hence skip the dilly-dally and look for all matches straight away.
|
||||
should_scan_tx
|
||||
self.get_buffer_for_full_scan_tx
|
||||
.send(ProjectPath {
|
||||
worktree_id: snapshot.id(),
|
||||
path: entry.path.clone(),
|
||||
|
||||
@@ -190,19 +190,6 @@ message GitRenameBranch {
|
||||
string new_name = 4;
|
||||
}
|
||||
|
||||
message GitCreateRemote {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
string remote_name = 3;
|
||||
string remote_url = 4;
|
||||
}
|
||||
|
||||
message GitRemoveRemote {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
string remote_name = 3;
|
||||
}
|
||||
|
||||
message GitDeleteBranch {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
|
||||
@@ -437,18 +437,13 @@ message Envelope {
|
||||
OpenImageResponse open_image_response = 392;
|
||||
CreateImageForPeer create_image_for_peer = 393;
|
||||
|
||||
|
||||
GitFileHistory git_file_history = 397;
|
||||
GitFileHistoryResponse git_file_history_response = 398;
|
||||
|
||||
RunGitHook run_git_hook = 399;
|
||||
|
||||
GitDeleteBranch git_delete_branch = 400;
|
||||
|
||||
ExternalExtensionAgentsUpdated external_extension_agents_updated = 401;
|
||||
|
||||
GitCreateRemote git_create_remote = 402;
|
||||
GitRemoveRemote git_remove_remote = 403;// current max
|
||||
ExternalExtensionAgentsUpdated external_extension_agents_updated = 401; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88, 396;
|
||||
|
||||
@@ -305,8 +305,6 @@ messages!(
|
||||
(RemoteMessageResponse, Background),
|
||||
(AskPassRequest, Background),
|
||||
(AskPassResponse, Background),
|
||||
(GitCreateRemote, Background),
|
||||
(GitRemoveRemote, Background),
|
||||
(GitCreateBranch, Background),
|
||||
(GitChangeBranch, Background),
|
||||
(GitRenameBranch, Background),
|
||||
@@ -506,8 +504,6 @@ request_messages!(
|
||||
(GetRemotes, GetRemotesResponse),
|
||||
(Pull, RemoteMessageResponse),
|
||||
(AskPassRequest, AskPassResponse),
|
||||
(GitCreateRemote, Ack),
|
||||
(GitRemoveRemote, Ack),
|
||||
(GitCreateBranch, Ack),
|
||||
(GitChangeBranch, Ack),
|
||||
(GitRenameBranch, Ack),
|
||||
@@ -680,8 +676,6 @@ entity_messages!(
|
||||
GitChangeBranch,
|
||||
GitRenameBranch,
|
||||
GitCreateBranch,
|
||||
GitCreateRemote,
|
||||
GitRemoveRemote,
|
||||
CheckForPushedCommits,
|
||||
GitDiff,
|
||||
GitInit,
|
||||
|
||||
@@ -43,6 +43,7 @@ urlencoding.workspace = true
|
||||
util.workspace = true
|
||||
which.workspace = true
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
fs = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -328,15 +328,8 @@ impl RemoteClient {
|
||||
let (incoming_tx, incoming_rx) = mpsc::unbounded::<Envelope>();
|
||||
let (connection_activity_tx, connection_activity_rx) = mpsc::channel::<()>(1);
|
||||
|
||||
let client = cx.update(|cx| {
|
||||
ChannelClient::new(
|
||||
incoming_rx,
|
||||
outgoing_tx,
|
||||
cx,
|
||||
"client",
|
||||
remote_connection.has_wsl_interop(),
|
||||
)
|
||||
})?;
|
||||
let client =
|
||||
cx.update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "client"))?;
|
||||
|
||||
let path_style = remote_connection.path_style();
|
||||
let this = cx.new(|_| Self {
|
||||
@@ -427,9 +420,8 @@ impl RemoteClient {
|
||||
outgoing_tx: mpsc::UnboundedSender<Envelope>,
|
||||
cx: &App,
|
||||
name: &'static str,
|
||||
has_wsl_interop: bool,
|
||||
) -> AnyProtoClient {
|
||||
ChannelClient::new(incoming_rx, outgoing_tx, cx, name, has_wsl_interop).into()
|
||||
ChannelClient::new(incoming_rx, outgoing_tx, cx, name).into()
|
||||
}
|
||||
|
||||
pub fn shutdown_processes<T: RequestMessage>(
|
||||
@@ -929,8 +921,8 @@ impl RemoteClient {
|
||||
});
|
||||
let (outgoing_tx, _) = mpsc::unbounded::<Envelope>();
|
||||
let (_, incoming_rx) = mpsc::unbounded::<Envelope>();
|
||||
let server_client = server_cx
|
||||
.update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "fake-server", false));
|
||||
let server_client =
|
||||
server_cx.update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "fake-server"));
|
||||
let connection: Arc<dyn RemoteConnection> = Arc::new(fake::FakeRemoteConnection {
|
||||
connection_options: opts.clone(),
|
||||
server_cx: fake::SendableCx::new(server_cx),
|
||||
@@ -1148,7 +1140,6 @@ pub trait RemoteConnection: Send + Sync {
|
||||
fn path_style(&self) -> PathStyle;
|
||||
fn shell(&self) -> String;
|
||||
fn default_system_shell(&self) -> String;
|
||||
fn has_wsl_interop(&self) -> bool;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fn simulate_disconnect(&self, _: &AsyncApp) {}
|
||||
@@ -1197,7 +1188,6 @@ struct ChannelClient {
|
||||
name: &'static str,
|
||||
task: Mutex<Task<Result<()>>>,
|
||||
remote_started: Signal<()>,
|
||||
has_wsl_interop: bool,
|
||||
}
|
||||
|
||||
impl ChannelClient {
|
||||
@@ -1206,7 +1196,6 @@ impl ChannelClient {
|
||||
outgoing_tx: mpsc::UnboundedSender<Envelope>,
|
||||
cx: &App,
|
||||
name: &'static str,
|
||||
has_wsl_interop: bool,
|
||||
) -> Arc<Self> {
|
||||
Arc::new_cyclic(|this| Self {
|
||||
outgoing_tx: Mutex::new(outgoing_tx),
|
||||
@@ -1222,7 +1211,6 @@ impl ChannelClient {
|
||||
&cx.to_async(),
|
||||
)),
|
||||
remote_started: Signal::new(cx),
|
||||
has_wsl_interop,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1501,10 +1489,6 @@ impl ProtoClient for ChannelClient {
|
||||
fn is_via_collab(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn has_wsl_interop(&self) -> bool {
|
||||
self.has_wsl_interop
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@@ -1668,10 +1652,6 @@ mod fake {
|
||||
fn default_system_shell(&self) -> String {
|
||||
"sh".to_owned()
|
||||
}
|
||||
|
||||
fn has_wsl_interop(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Delegate;
|
||||
|
||||
@@ -131,7 +131,11 @@ async fn build_remote_server_from_source(
|
||||
let build_remote_server =
|
||||
std::env::var("ZED_BUILD_REMOTE_SERVER").unwrap_or("nocompress".into());
|
||||
|
||||
if let "false" | "no" | "off" | "0" = &*build_remote_server {
|
||||
if build_remote_server == "false"
|
||||
|| build_remote_server == "no"
|
||||
|| build_remote_server == "off"
|
||||
|| build_remote_server == "0"
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
||||
@@ -394,10 +394,6 @@ impl RemoteConnection for SshRemoteConnection {
|
||||
fn path_style(&self) -> PathStyle {
|
||||
self.ssh_path_style
|
||||
}
|
||||
|
||||
fn has_wsl_interop(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl SshRemoteConnection {
|
||||
|
||||
@@ -47,7 +47,6 @@ pub(crate) struct WslRemoteConnection {
|
||||
shell: String,
|
||||
shell_kind: ShellKind,
|
||||
default_system_shell: String,
|
||||
has_wsl_interop: bool,
|
||||
connection_options: WslConnectionOptions,
|
||||
}
|
||||
|
||||
@@ -72,7 +71,6 @@ impl WslRemoteConnection {
|
||||
shell: String::new(),
|
||||
shell_kind: ShellKind::Posix,
|
||||
default_system_shell: String::from("/bin/sh"),
|
||||
has_wsl_interop: false,
|
||||
};
|
||||
delegate.set_status(Some("Detecting WSL environment"), cx);
|
||||
this.shell = this
|
||||
@@ -81,15 +79,6 @@ impl WslRemoteConnection {
|
||||
.context("failed detecting shell")?;
|
||||
log::info!("Remote shell discovered: {}", this.shell);
|
||||
this.shell_kind = ShellKind::new(&this.shell, false);
|
||||
this.has_wsl_interop = this.detect_has_wsl_interop().await.unwrap_or_default();
|
||||
log::info!(
|
||||
"Remote has wsl interop {}",
|
||||
if this.has_wsl_interop {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
}
|
||||
);
|
||||
this.platform = this
|
||||
.detect_platform()
|
||||
.await
|
||||
@@ -126,14 +115,6 @@ impl WslRemoteConnection {
|
||||
.unwrap_or_else(|| "/bin/sh".to_string()))
|
||||
}
|
||||
|
||||
async fn detect_has_wsl_interop(&self) -> Result<bool> {
|
||||
Ok(self
|
||||
.run_wsl_command_with_output("cat", &["/proc/sys/fs/binfmt_misc/WSLInterop"])
|
||||
.await
|
||||
.inspect_err(|err| log::error!("Failed to detect wsl interop: {err}"))?
|
||||
.contains("enabled"))
|
||||
}
|
||||
|
||||
async fn windows_path_to_wsl_path(&self, source: &Path) -> Result<String> {
|
||||
windows_path_to_wsl_path_impl(&self.connection_options, source).await
|
||||
}
|
||||
@@ -336,7 +317,6 @@ impl RemoteConnection for WslRemoteConnection {
|
||||
proxy_args.push(format!("{}={}", env_var, value));
|
||||
}
|
||||
}
|
||||
|
||||
proxy_args.push(remote_binary_path.display(PathStyle::Posix).into_owned());
|
||||
proxy_args.push("proxy".to_owned());
|
||||
proxy_args.push("--identifier".to_owned());
|
||||
@@ -509,10 +489,6 @@ impl RemoteConnection for WslRemoteConnection {
|
||||
fn default_system_shell(&self) -> String {
|
||||
self.default_system_shell.clone()
|
||||
}
|
||||
|
||||
fn has_wsl_interop(&self) -> bool {
|
||||
self.has_wsl_interop
|
||||
}
|
||||
}
|
||||
|
||||
/// `wslpath` is a executable available in WSL, it's a linux binary.
|
||||
|
||||
@@ -199,7 +199,6 @@ fn start_server(
|
||||
listeners: ServerListeners,
|
||||
log_rx: Receiver<Vec<u8>>,
|
||||
cx: &mut App,
|
||||
is_wsl_interop: bool,
|
||||
) -> AnyProtoClient {
|
||||
// This is the server idle timeout. If no connection comes in this timeout, the server will shut down.
|
||||
const IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10 * 60);
|
||||
@@ -319,7 +318,7 @@ fn start_server(
|
||||
})
|
||||
.detach();
|
||||
|
||||
RemoteClient::proto_client_from_channels(incoming_rx, outgoing_tx, cx, "server", is_wsl_interop)
|
||||
RemoteClient::proto_client_from_channels(incoming_rx, outgoing_tx, cx, "server")
|
||||
}
|
||||
|
||||
fn init_paths() -> anyhow::Result<()> {
|
||||
@@ -408,15 +407,8 @@ pub fn execute_run(
|
||||
|
||||
HeadlessProject::init(cx);
|
||||
|
||||
let is_wsl_interop = if cfg!(target_os = "linux") {
|
||||
// See: https://learn.microsoft.com/en-us/windows/wsl/filesystems#disable-interoperability
|
||||
matches!(std::fs::read_to_string("/proc/sys/fs/binfmt_misc/WSLInterop"), Ok(s) if s.contains("enabled"))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
log::info!("gpui app started, initializing server");
|
||||
let session = start_server(listeners, log_rx, cx, is_wsl_interop);
|
||||
let session = start_server(listeners, log_rx, cx);
|
||||
|
||||
GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
|
||||
git_hosting_providers::init(cx);
|
||||
|
||||
@@ -59,7 +59,6 @@ pub trait ProtoClient: Send + Sync {
|
||||
fn message_handler_set(&self) -> &parking_lot::Mutex<ProtoMessageHandlerSet>;
|
||||
|
||||
fn is_via_collab(&self) -> bool;
|
||||
fn has_wsl_interop(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -511,10 +510,6 @@ impl AnyProtoClient {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn has_wsl_interop(&self) -> bool {
|
||||
self.0.client.has_wsl_interop()
|
||||
}
|
||||
}
|
||||
|
||||
fn to_any_envelope<T: EnvelopedMessage>(
|
||||
|
||||
@@ -255,6 +255,7 @@ impl JsonSchema for LanguageModelProviderSetting {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"amazon-bedrock",
|
||||
"anthropic",
|
||||
"copilot_chat",
|
||||
"deepseek",
|
||||
"google",
|
||||
|
||||
@@ -20,12 +20,6 @@ pub struct ExtensionSettingsContent {
|
||||
pub auto_update_extensions: HashMap<Arc<str>, bool>,
|
||||
/// The capabilities granted to extensions.
|
||||
pub granted_extension_capabilities: Option<Vec<ExtensionCapabilityContent>>,
|
||||
/// Extension language model providers that are allowed to read API keys from
|
||||
/// environment variables. Each entry is a provider ID in the format
|
||||
/// "extension_id:provider_id" (e.g., "openai:openai").
|
||||
///
|
||||
/// Default: []
|
||||
pub allowed_env_var_providers: Option<Vec<Arc<str>>>,
|
||||
}
|
||||
|
||||
/// A capability for an extension.
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::sync::Arc;
|
||||
#[with_fallible_options]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct AllLanguageModelSettingsContent {
|
||||
pub anthropic: Option<AnthropicSettingsContent>,
|
||||
pub bedrock: Option<AmazonBedrockSettingsContent>,
|
||||
pub deepseek: Option<DeepseekSettingsContent>,
|
||||
pub google: Option<GoogleSettingsContent>,
|
||||
@@ -23,6 +24,35 @@ pub struct AllLanguageModelSettingsContent {
|
||||
pub zed_dot_dev: Option<ZedDotDevSettingsContent>,
|
||||
}
|
||||
|
||||
#[with_fallible_options]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct AnthropicSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<AnthropicAvailableModel>>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
/// 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<String>,
|
||||
/// Configuration of Anthropic's caching API.
|
||||
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
|
||||
pub max_output_tokens: Option<u64>,
|
||||
#[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
|
||||
pub default_temperature: Option<f32>,
|
||||
#[serde(default)]
|
||||
pub extra_beta_headers: Vec<String>,
|
||||
/// The model's mode (e.g. thinking)
|
||||
pub mode: Option<ModelMode>,
|
||||
}
|
||||
|
||||
#[with_fallible_options]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct AmazonBedrockSettingsContent {
|
||||
@@ -31,7 +61,6 @@ pub struct AmazonBedrockSettingsContent {
|
||||
pub region: Option<String>,
|
||||
pub profile: Option<String>,
|
||||
pub authentication_method: Option<BedrockAuthMethodContent>,
|
||||
pub allow_global: Option<bool>,
|
||||
}
|
||||
|
||||
#[with_fallible_options]
|
||||
|
||||
@@ -588,20 +588,19 @@ impl ToolchainSelector {
|
||||
.worktree_for_id(worktree_id, cx)?
|
||||
.read(cx)
|
||||
.abs_path();
|
||||
let workspace_id = workspace.database_id()?;
|
||||
let weak = workspace.weak_handle();
|
||||
cx.spawn_in(window, async move |workspace, cx| {
|
||||
let active_toolchain = project
|
||||
.read_with(cx, |this, cx| {
|
||||
this.active_toolchain(
|
||||
ProjectPath {
|
||||
worktree_id,
|
||||
path: relative_path.clone(),
|
||||
},
|
||||
language_name.clone(),
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await;
|
||||
let active_toolchain = workspace::WORKSPACE_DB
|
||||
.toolchain(
|
||||
workspace_id,
|
||||
worktree_id,
|
||||
relative_path.clone(),
|
||||
language_name.clone(),
|
||||
)
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
workspace
|
||||
.update_in(cx, |this, window, cx| {
|
||||
this.toggle_modal(window, cx, move |window, cx| {
|
||||
@@ -619,7 +618,6 @@ impl ToolchainSelector {
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
||||
@@ -1656,6 +1656,49 @@ impl WorkspaceDb {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn toolchain(
|
||||
&self,
|
||||
workspace_id: WorkspaceId,
|
||||
worktree_id: WorktreeId,
|
||||
relative_worktree_path: Arc<RelPath>,
|
||||
language_name: LanguageName,
|
||||
) -> Result<Option<Toolchain>> {
|
||||
self.write(move |this| {
|
||||
let mut select = this
|
||||
.select_bound(sql!(
|
||||
SELECT
|
||||
name, path, raw_json
|
||||
FROM toolchains
|
||||
WHERE
|
||||
workspace_id = ? AND
|
||||
language_name = ? AND
|
||||
worktree_id = ? AND
|
||||
relative_worktree_path = ?
|
||||
))
|
||||
.context("select toolchain")?;
|
||||
|
||||
let toolchain: Vec<(String, String, String)> = select((
|
||||
workspace_id,
|
||||
language_name.as_ref().to_string(),
|
||||
worktree_id.to_usize(),
|
||||
relative_worktree_path.as_unix_str().to_string(),
|
||||
))?;
|
||||
|
||||
Ok(toolchain
|
||||
.into_iter()
|
||||
.next()
|
||||
.and_then(|(name, path, raw_json)| {
|
||||
Some(Toolchain {
|
||||
name: name.into(),
|
||||
path: path.into(),
|
||||
language_name,
|
||||
as_json: serde_json::Value::from_str(&raw_json).ok()?,
|
||||
})
|
||||
}))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn toolchains(
|
||||
&self,
|
||||
workspace_id: WorkspaceId,
|
||||
|
||||
@@ -52,7 +52,7 @@ use std::{
|
||||
fmt,
|
||||
future::Future,
|
||||
mem::{self},
|
||||
ops::{Deref, DerefMut, Range},
|
||||
ops::{Deref, DerefMut},
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::{
|
||||
@@ -3877,35 +3877,29 @@ impl BackgroundScanner {
|
||||
abs_paths.dedup_by(|a, b| a.starts_with(b));
|
||||
{
|
||||
let snapshot = &self.state.lock().await.snapshot;
|
||||
abs_paths.retain(|abs_path| {
|
||||
let abs_path = &SanitizedPath::new(abs_path);
|
||||
|
||||
let mut ranges_to_drop = SmallVec::<[Range<usize>; 4]>::new();
|
||||
|
||||
fn skip_ix(ranges: &mut SmallVec<[Range<usize>; 4]>, ix: usize) {
|
||||
if let Some(last_range) = ranges.last_mut()
|
||||
&& last_range.end == ix
|
||||
{
|
||||
last_range.end += 1;
|
||||
} else {
|
||||
ranges.push(ix..ix + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (ix, abs_path) in abs_paths.iter().enumerate() {
|
||||
let abs_path = &SanitizedPath::new(&abs_path);
|
||||
|
||||
{
|
||||
let mut is_git_related = false;
|
||||
let mut dot_git_paths = None;
|
||||
|
||||
for ancestor in abs_path.as_path().ancestors() {
|
||||
let dot_git_paths = self.executor.block(maybe!(async {
|
||||
let mut path = None;
|
||||
for ancestor in abs_path.as_path().ancestors() {
|
||||
|
||||
if is_git_dir(ancestor, self.fs.as_ref()).await {
|
||||
let path_in_git_dir = abs_path
|
||||
.as_path()
|
||||
.strip_prefix(ancestor)
|
||||
.expect("stripping off the ancestor");
|
||||
dot_git_paths = Some((ancestor.to_owned(), path_in_git_dir.to_owned()));
|
||||
break;
|
||||
path = Some((ancestor.to_owned(), path_in_git_dir.to_owned()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path
|
||||
|
||||
}));
|
||||
|
||||
if let Some((dot_git_abs_path, path_in_git_dir)) = dot_git_paths {
|
||||
if skipped_files_in_dot_git
|
||||
@@ -3915,11 +3909,8 @@ impl BackgroundScanner {
|
||||
path_in_git_dir.starts_with(skipped_git_subdir)
|
||||
})
|
||||
{
|
||||
log::debug!(
|
||||
"ignoring event {abs_path:?} as it's in the .git directory among skipped files or directories"
|
||||
);
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
log::debug!("ignoring event {abs_path:?} as it's in the .git directory among skipped files or directories");
|
||||
return false;
|
||||
}
|
||||
|
||||
is_git_related = true;
|
||||
@@ -3928,7 +3919,8 @@ impl BackgroundScanner {
|
||||
}
|
||||
}
|
||||
|
||||
let relative_path = if let Ok(path) = abs_path.strip_prefix(&root_canonical_path)
|
||||
let relative_path = if let Ok(path) =
|
||||
abs_path.strip_prefix(&root_canonical_path)
|
||||
&& let Ok(path) = RelPath::new(path, PathStyle::local())
|
||||
{
|
||||
path
|
||||
@@ -3939,11 +3931,10 @@ impl BackgroundScanner {
|
||||
);
|
||||
} else {
|
||||
log::error!(
|
||||
"ignoring event {abs_path:?} outside of root path {root_canonical_path:?}",
|
||||
"ignoring event {abs_path:?} outside of root path {root_canonical_path:?}",
|
||||
);
|
||||
}
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
return false;
|
||||
};
|
||||
|
||||
if abs_path.file_name() == Some(OsStr::new(GITIGNORE)) {
|
||||
@@ -3967,26 +3958,21 @@ impl BackgroundScanner {
|
||||
});
|
||||
if !parent_dir_is_loaded {
|
||||
log::debug!("ignoring event {relative_path:?} within unloaded directory");
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.settings.is_path_excluded(&relative_path) {
|
||||
if !is_git_related {
|
||||
log::debug!("ignoring FS event for excluded path {relative_path:?}");
|
||||
}
|
||||
skip_ix(&mut ranges_to_drop, ix);
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
relative_paths.push(relative_path.into_arc());
|
||||
true
|
||||
}
|
||||
|
||||
for range_to_drop in ranges_to_drop.into_iter().rev() {
|
||||
abs_paths.drain(range_to_drop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if relative_paths.is_empty() && dot_git_abs_paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,5 @@
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.photos-library</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -215,10 +215,6 @@ pub mod git {
|
||||
Switch,
|
||||
/// Selects a different repository.
|
||||
SelectRepo,
|
||||
/// Filter remotes.
|
||||
FilterRemotes,
|
||||
/// Create a git remote.
|
||||
CreateRemote,
|
||||
/// Opens the git branch selector.
|
||||
#[action(deprecated_aliases = ["branches::OpenRecent"])]
|
||||
Branch,
|
||||
|
||||
@@ -89,32 +89,12 @@ To do this:
|
||||
|
||||
#### Cross-Region Inference
|
||||
|
||||
The Zed implementation of Amazon Bedrock uses [Cross-Region inference](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html) to improve availability and throughput.
|
||||
The Zed implementation of Amazon Bedrock uses [Cross-Region inference](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html) for all the models and region combinations that support it.
|
||||
With Cross-Region inference, you can distribute traffic across multiple AWS Regions, enabling higher throughput.
|
||||
|
||||
##### Regional vs Global Inference Profiles
|
||||
|
||||
Bedrock supports two types of cross-region inference profiles:
|
||||
|
||||
- **Regional profiles** (default): Route requests within a specific geography (US, EU, APAC). For example, `us-east-1` uses the `us.*` profile which routes across `us-east-1`, `us-east-2`, and `us-west-2`.
|
||||
- **Global profiles**: Route requests across all commercial AWS Regions for maximum availability and performance.
|
||||
|
||||
By default, Zed uses **regional profiles** which keep your data within the same geography. You can opt into global profiles by adding `"allow_global": true` to your Bedrock configuration:
|
||||
|
||||
```json [settings]
|
||||
{
|
||||
"language_models": {
|
||||
"bedrock": {
|
||||
"authentication_method": "named_profile",
|
||||
"region": "your-aws-region",
|
||||
"profile": "your-profile-name",
|
||||
"allow_global": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Only select newer models support global inference profiles. See the [AWS Bedrock supported models documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system) for the current list of models that support global inference. If you encounter availability issues with a model in your region, enabling `allow_global` may resolve them.
|
||||
For example, if you use `Claude Sonnet 3.7 Thinking` from `us-east-1`, it may be processed across the US regions, namely: `us-east-1`, `us-east-2`, or `us-west-2`.
|
||||
Cross-Region inference requests are kept within the AWS Regions that are part of the geography where the data originally resides.
|
||||
For example, a request made within the US is kept within the AWS Regions in the US.
|
||||
|
||||
Although the data remains stored only in the source Region, your input prompts and output results might move outside of your source Region during cross-Region inference.
|
||||
All data will be transmitted encrypted across Amazon's secure network.
|
||||
|
||||
@@ -2626,6 +2626,9 @@ 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"
|
||||
},
|
||||
|
||||
823
extensions/anthropic/Cargo.lock
generated
823
extensions/anthropic/Cargo.lock
generated
@@ -1,823 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anthropic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"zed_extension_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "auditable-serde"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auditable-serde",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"url",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad19eec017904e04c60719592a803ee5da76cb51c81e3f6fbf9457f59db49799"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "anthropic"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "Apache-2.0"
|
||||
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
path = "src/anthropic.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
zed_extension_api = { path = "../../crates/extension_api" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
@@ -1,13 +0,0 @@
|
||||
id = "anthropic"
|
||||
name = "Anthropic"
|
||||
description = "Anthropic Claude LLM provider for Zed."
|
||||
version = "0.1.0"
|
||||
schema_version = 1
|
||||
authors = ["Zed Team"]
|
||||
repository = "https://github.com/zed-industries/zed"
|
||||
|
||||
[language_model_providers.anthropic]
|
||||
name = "Anthropic"
|
||||
|
||||
[language_model_providers.anthropic.auth]
|
||||
env_var = "ANTHROPIC_API_KEY"
|
||||
@@ -1,803 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zed_extension_api::http_client::{HttpMethod, HttpRequest, HttpResponseStream, RedirectPolicy};
|
||||
use zed_extension_api::{self as zed, *};
|
||||
|
||||
struct AnthropicProvider {
|
||||
streams: Mutex<HashMap<String, StreamState>>,
|
||||
next_stream_id: Mutex<u64>,
|
||||
}
|
||||
|
||||
struct StreamState {
|
||||
response_stream: Option<HttpResponseStream>,
|
||||
buffer: String,
|
||||
started: bool,
|
||||
current_tool_use: Option<ToolUseState>,
|
||||
stop_reason: Option<LlmStopReason>,
|
||||
pending_signature: Option<String>,
|
||||
}
|
||||
|
||||
struct ToolUseState {
|
||||
id: String,
|
||||
name: String,
|
||||
input_json: String,
|
||||
}
|
||||
|
||||
struct ModelDefinition {
|
||||
real_id: &'static str,
|
||||
display_name: &'static str,
|
||||
max_tokens: u64,
|
||||
max_output_tokens: u64,
|
||||
supports_images: bool,
|
||||
supports_thinking: bool,
|
||||
is_default: bool,
|
||||
is_default_fast: bool,
|
||||
}
|
||||
|
||||
const MODELS: &[ModelDefinition] = &[
|
||||
ModelDefinition {
|
||||
real_id: "claude-opus-4-5-20251101",
|
||||
display_name: "Claude Opus 4.5",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: false,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-opus-4-5-20251101",
|
||||
display_name: "Claude Opus 4.5 Thinking",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-sonnet-4-5-20250929",
|
||||
display_name: "Claude Sonnet 4.5",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: false,
|
||||
is_default: true,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-sonnet-4-5-20250929",
|
||||
display_name: "Claude Sonnet 4.5 Thinking",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-sonnet-4-20250514",
|
||||
display_name: "Claude Sonnet 4",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: false,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-sonnet-4-20250514",
|
||||
display_name: "Claude Sonnet 4 Thinking",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-haiku-4-5-20251001",
|
||||
display_name: "Claude Haiku 4.5",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 64_000,
|
||||
supports_images: true,
|
||||
supports_thinking: false,
|
||||
is_default: false,
|
||||
is_default_fast: true,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-haiku-4-5-20251001",
|
||||
display_name: "Claude Haiku 4.5 Thinking",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 64_000,
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-3-5-sonnet-latest",
|
||||
display_name: "Claude 3.5 Sonnet",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: false,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "claude-3-5-haiku-latest",
|
||||
display_name: "Claude 3.5 Haiku",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: 8_192,
|
||||
supports_images: true,
|
||||
supports_thinking: false,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
];
|
||||
|
||||
fn get_model_definition(display_name: &str) -> Option<&'static ModelDefinition> {
|
||||
MODELS.iter().find(|m| m.display_name == display_name)
|
||||
}
|
||||
|
||||
// Anthropic API Request Types
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AnthropicRequest {
|
||||
model: String,
|
||||
max_tokens: u64,
|
||||
messages: Vec<AnthropicMessage>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
system: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
thinking: Option<AnthropicThinking>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
tools: Vec<AnthropicTool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tool_choice: Option<AnthropicToolChoice>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
stop_sequences: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
temperature: Option<f32>,
|
||||
stream: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AnthropicThinking {
|
||||
#[serde(rename = "type")]
|
||||
thinking_type: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
budget_tokens: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AnthropicMessage {
|
||||
role: String,
|
||||
content: Vec<AnthropicContent>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(tag = "type")]
|
||||
enum AnthropicContent {
|
||||
#[serde(rename = "text")]
|
||||
Text { text: String },
|
||||
#[serde(rename = "thinking")]
|
||||
Thinking { thinking: String, signature: String },
|
||||
#[serde(rename = "redacted_thinking")]
|
||||
RedactedThinking { data: String },
|
||||
#[serde(rename = "image")]
|
||||
Image { source: AnthropicImageSource },
|
||||
#[serde(rename = "tool_use")]
|
||||
ToolUse {
|
||||
id: String,
|
||||
name: String,
|
||||
input: serde_json::Value,
|
||||
},
|
||||
#[serde(rename = "tool_result")]
|
||||
ToolResult {
|
||||
tool_use_id: String,
|
||||
is_error: bool,
|
||||
content: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
struct AnthropicImageSource {
|
||||
#[serde(rename = "type")]
|
||||
source_type: String,
|
||||
media_type: String,
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AnthropicTool {
|
||||
name: String,
|
||||
description: String,
|
||||
input_schema: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
enum AnthropicToolChoice {
|
||||
Auto,
|
||||
Any,
|
||||
None,
|
||||
}
|
||||
|
||||
// Anthropic API Response Types
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
#[allow(dead_code)]
|
||||
enum AnthropicEvent {
|
||||
#[serde(rename = "message_start")]
|
||||
MessageStart { message: AnthropicMessageResponse },
|
||||
#[serde(rename = "content_block_start")]
|
||||
ContentBlockStart {
|
||||
index: usize,
|
||||
content_block: AnthropicContentBlock,
|
||||
},
|
||||
#[serde(rename = "content_block_delta")]
|
||||
ContentBlockDelta { index: usize, delta: AnthropicDelta },
|
||||
#[serde(rename = "content_block_stop")]
|
||||
ContentBlockStop { index: usize },
|
||||
#[serde(rename = "message_delta")]
|
||||
MessageDelta {
|
||||
delta: AnthropicMessageDelta,
|
||||
usage: AnthropicUsage,
|
||||
},
|
||||
#[serde(rename = "message_stop")]
|
||||
MessageStop,
|
||||
#[serde(rename = "ping")]
|
||||
Ping,
|
||||
#[serde(rename = "error")]
|
||||
Error { error: AnthropicApiError },
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct AnthropicMessageResponse {
|
||||
#[allow(dead_code)]
|
||||
id: String,
|
||||
#[allow(dead_code)]
|
||||
role: String,
|
||||
#[serde(default)]
|
||||
usage: AnthropicUsage,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
enum AnthropicContentBlock {
|
||||
#[serde(rename = "text")]
|
||||
Text { text: String },
|
||||
#[serde(rename = "thinking")]
|
||||
Thinking { thinking: String },
|
||||
#[serde(rename = "redacted_thinking")]
|
||||
RedactedThinking { data: String },
|
||||
#[serde(rename = "tool_use")]
|
||||
ToolUse { id: String, name: String },
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
enum AnthropicDelta {
|
||||
#[serde(rename = "text_delta")]
|
||||
TextDelta { text: String },
|
||||
#[serde(rename = "thinking_delta")]
|
||||
ThinkingDelta { thinking: String },
|
||||
#[serde(rename = "signature_delta")]
|
||||
SignatureDelta { signature: String },
|
||||
#[serde(rename = "input_json_delta")]
|
||||
InputJsonDelta { partial_json: String },
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct AnthropicMessageDelta {
|
||||
stop_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
struct AnthropicUsage {
|
||||
#[serde(default)]
|
||||
input_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
output_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
cache_creation_input_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
cache_read_input_tokens: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct AnthropicApiError {
|
||||
#[serde(rename = "type")]
|
||||
#[allow(dead_code)]
|
||||
error_type: String,
|
||||
message: String,
|
||||
}
|
||||
|
||||
fn convert_request(
|
||||
model_id: &str,
|
||||
request: &LlmCompletionRequest,
|
||||
) -> Result<AnthropicRequest, String> {
|
||||
let model_def =
|
||||
get_model_definition(model_id).ok_or_else(|| format!("Unknown model: {}", model_id))?;
|
||||
|
||||
let mut messages: Vec<AnthropicMessage> = Vec::new();
|
||||
let mut system_message = String::new();
|
||||
|
||||
for msg in &request.messages {
|
||||
match msg.role {
|
||||
LlmMessageRole::System => {
|
||||
for content in &msg.content {
|
||||
if let LlmMessageContent::Text(text) = content {
|
||||
if !system_message.is_empty() {
|
||||
system_message.push('\n');
|
||||
}
|
||||
system_message.push_str(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
LlmMessageRole::User => {
|
||||
let mut contents: Vec<AnthropicContent> = Vec::new();
|
||||
|
||||
for content in &msg.content {
|
||||
match content {
|
||||
LlmMessageContent::Text(text) => {
|
||||
if !text.is_empty() {
|
||||
contents.push(AnthropicContent::Text { text: text.clone() });
|
||||
}
|
||||
}
|
||||
LlmMessageContent::Image(img) => {
|
||||
contents.push(AnthropicContent::Image {
|
||||
source: AnthropicImageSource {
|
||||
source_type: "base64".to_string(),
|
||||
media_type: "image/png".to_string(),
|
||||
data: img.source.clone(),
|
||||
},
|
||||
});
|
||||
}
|
||||
LlmMessageContent::ToolResult(result) => {
|
||||
let content_text = match &result.content {
|
||||
LlmToolResultContent::Text(t) => t.clone(),
|
||||
LlmToolResultContent::Image(_) => "[Image]".to_string(),
|
||||
};
|
||||
contents.push(AnthropicContent::ToolResult {
|
||||
tool_use_id: result.tool_use_id.clone(),
|
||||
is_error: result.is_error,
|
||||
content: content_text,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !contents.is_empty() {
|
||||
messages.push(AnthropicMessage {
|
||||
role: "user".to_string(),
|
||||
content: contents,
|
||||
});
|
||||
}
|
||||
}
|
||||
LlmMessageRole::Assistant => {
|
||||
let mut contents: Vec<AnthropicContent> = Vec::new();
|
||||
|
||||
for content in &msg.content {
|
||||
match content {
|
||||
LlmMessageContent::Text(text) => {
|
||||
if !text.is_empty() {
|
||||
contents.push(AnthropicContent::Text { text: text.clone() });
|
||||
}
|
||||
}
|
||||
LlmMessageContent::ToolUse(tool_use) => {
|
||||
let input: serde_json::Value =
|
||||
serde_json::from_str(&tool_use.input).unwrap_or_default();
|
||||
contents.push(AnthropicContent::ToolUse {
|
||||
id: tool_use.id.clone(),
|
||||
name: tool_use.name.clone(),
|
||||
input,
|
||||
});
|
||||
}
|
||||
LlmMessageContent::Thinking(thinking) => {
|
||||
if !thinking.text.is_empty() {
|
||||
contents.push(AnthropicContent::Thinking {
|
||||
thinking: thinking.text.clone(),
|
||||
signature: thinking.signature.clone().unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
LlmMessageContent::RedactedThinking(data) => {
|
||||
if !data.is_empty() {
|
||||
contents.push(AnthropicContent::RedactedThinking {
|
||||
data: data.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !contents.is_empty() {
|
||||
messages.push(AnthropicMessage {
|
||||
role: "assistant".to_string(),
|
||||
content: contents,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tools: Vec<AnthropicTool> = request
|
||||
.tools
|
||||
.iter()
|
||||
.map(|t| AnthropicTool {
|
||||
name: t.name.clone(),
|
||||
description: t.description.clone(),
|
||||
input_schema: serde_json::from_str(&t.input_schema)
|
||||
.unwrap_or(serde_json::Value::Object(Default::default())),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let tool_choice = request.tool_choice.as_ref().map(|tc| match tc {
|
||||
LlmToolChoice::Auto => AnthropicToolChoice::Auto,
|
||||
LlmToolChoice::Any => AnthropicToolChoice::Any,
|
||||
LlmToolChoice::None => AnthropicToolChoice::None,
|
||||
});
|
||||
|
||||
let thinking = if model_def.supports_thinking && request.thinking_allowed {
|
||||
Some(AnthropicThinking {
|
||||
thinking_type: "enabled".to_string(),
|
||||
budget_tokens: Some(4096),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(AnthropicRequest {
|
||||
model: model_def.real_id.to_string(),
|
||||
max_tokens: model_def.max_output_tokens,
|
||||
messages,
|
||||
system: if system_message.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(system_message)
|
||||
},
|
||||
thinking,
|
||||
tools,
|
||||
tool_choice,
|
||||
stop_sequences: request.stop_sequences.clone(),
|
||||
temperature: request.temperature,
|
||||
stream: true,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_sse_line(line: &str) -> Option<AnthropicEvent> {
|
||||
let data = line.strip_prefix("data: ")?;
|
||||
serde_json::from_str(data).ok()
|
||||
}
|
||||
|
||||
impl zed::Extension for AnthropicProvider {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
streams: Mutex::new(HashMap::new()),
|
||||
next_stream_id: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_providers(&self) -> Vec<LlmProviderInfo> {
|
||||
vec![LlmProviderInfo {
|
||||
id: "anthropic".into(),
|
||||
name: "Anthropic".into(),
|
||||
icon: Some("icons/anthropic.svg".into()),
|
||||
}]
|
||||
}
|
||||
|
||||
fn llm_provider_models(&self, _provider_id: &str) -> Result<Vec<LlmModelInfo>, String> {
|
||||
Ok(MODELS
|
||||
.iter()
|
||||
.map(|m| LlmModelInfo {
|
||||
id: m.display_name.to_string(),
|
||||
name: m.display_name.to_string(),
|
||||
max_token_count: m.max_tokens,
|
||||
max_output_tokens: Some(m.max_output_tokens),
|
||||
capabilities: LlmModelCapabilities {
|
||||
supports_images: m.supports_images,
|
||||
supports_tools: true,
|
||||
supports_tool_choice_auto: true,
|
||||
supports_tool_choice_any: true,
|
||||
supports_tool_choice_none: true,
|
||||
supports_thinking: m.supports_thinking,
|
||||
tool_input_format: LlmToolInputFormat::JsonSchema,
|
||||
},
|
||||
is_default: m.is_default,
|
||||
is_default_fast: m.is_default_fast,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
|
||||
llm_get_credential("anthropic").is_some()
|
||||
}
|
||||
|
||||
fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
|
||||
Some(
|
||||
r#"# Anthropic Setup
|
||||
|
||||
Welcome to **Anthropic**! This extension provides access to Claude models.
|
||||
|
||||
## Configuration
|
||||
|
||||
Enter your Anthropic API key below. You can get your API key at [console.anthropic.com](https://console.anthropic.com/).
|
||||
|
||||
## Available Models
|
||||
|
||||
| Display Name | Real Model | Context | Output |
|
||||
|--------------|------------|---------|--------|
|
||||
| Claude Opus 4.5 | claude-opus-4-5 | 200K | 8K |
|
||||
| Claude Opus 4.5 Thinking | claude-opus-4-5 | 200K | 8K |
|
||||
| Claude Sonnet 4.5 | claude-sonnet-4-5 | 200K | 8K |
|
||||
| Claude Sonnet 4.5 Thinking | claude-sonnet-4-5 | 200K | 8K |
|
||||
| Claude Sonnet 4 | claude-sonnet-4 | 200K | 8K |
|
||||
| Claude Sonnet 4 Thinking | claude-sonnet-4 | 200K | 8K |
|
||||
| Claude Haiku 4.5 | claude-haiku-4-5 | 200K | 64K |
|
||||
| Claude Haiku 4.5 Thinking | claude-haiku-4-5 | 200K | 64K |
|
||||
| Claude 3.5 Sonnet | claude-3-5-sonnet | 200K | 8K |
|
||||
| Claude 3.5 Haiku | claude-3-5-haiku | 200K | 8K |
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Full streaming support
|
||||
- ✅ Tool/function calling
|
||||
- ✅ Vision (image inputs)
|
||||
- ✅ Extended thinking support
|
||||
- ✅ All Claude models
|
||||
|
||||
## Pricing
|
||||
|
||||
Uses your Anthropic API credits. See [Anthropic pricing](https://www.anthropic.com/pricing) for details.
|
||||
"#
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
fn llm_provider_authenticate(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
let provided = llm_request_credential(
|
||||
"anthropic",
|
||||
LlmCredentialType::ApiKey,
|
||||
"Anthropic API Key",
|
||||
"sk-ant-...",
|
||||
)?;
|
||||
if provided {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Authentication cancelled".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_provider_reset_credentials(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
llm_delete_credential("anthropic")
|
||||
}
|
||||
|
||||
fn llm_stream_completion_start(
|
||||
&mut self,
|
||||
_provider_id: &str,
|
||||
model_id: &str,
|
||||
request: &LlmCompletionRequest,
|
||||
) -> Result<String, String> {
|
||||
let api_key = llm_get_credential("anthropic").ok_or_else(|| {
|
||||
"No API key configured. Please add your Anthropic API key in settings.".to_string()
|
||||
})?;
|
||||
|
||||
let anthropic_request = convert_request(model_id, request)?;
|
||||
|
||||
let body = serde_json::to_vec(&anthropic_request)
|
||||
.map_err(|e| format!("Failed to serialize request: {}", e))?;
|
||||
|
||||
let http_request = HttpRequest {
|
||||
method: HttpMethod::Post,
|
||||
url: "https://api.anthropic.com/v1/messages".to_string(),
|
||||
headers: vec![
|
||||
("Content-Type".to_string(), "application/json".to_string()),
|
||||
("x-api-key".to_string(), api_key),
|
||||
("anthropic-version".to_string(), "2023-06-01".to_string()),
|
||||
],
|
||||
body: Some(body),
|
||||
redirect_policy: RedirectPolicy::FollowAll,
|
||||
};
|
||||
|
||||
let response_stream = http_request
|
||||
.fetch_stream()
|
||||
.map_err(|e| format!("HTTP request failed: {}", e))?;
|
||||
|
||||
let stream_id = {
|
||||
let mut id_counter = self.next_stream_id.lock().unwrap();
|
||||
let id = format!("anthropic-stream-{}", *id_counter);
|
||||
*id_counter += 1;
|
||||
id
|
||||
};
|
||||
|
||||
self.streams.lock().unwrap().insert(
|
||||
stream_id.clone(),
|
||||
StreamState {
|
||||
response_stream: Some(response_stream),
|
||||
buffer: String::new(),
|
||||
started: false,
|
||||
current_tool_use: None,
|
||||
stop_reason: None,
|
||||
pending_signature: None,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(stream_id)
|
||||
}
|
||||
|
||||
fn llm_stream_completion_next(
|
||||
&mut self,
|
||||
stream_id: &str,
|
||||
) -> Result<Option<LlmCompletionEvent>, String> {
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
let state = streams
|
||||
.get_mut(stream_id)
|
||||
.ok_or_else(|| format!("Unknown stream: {}", stream_id))?;
|
||||
|
||||
if !state.started {
|
||||
state.started = true;
|
||||
return Ok(Some(LlmCompletionEvent::Started));
|
||||
}
|
||||
|
||||
let response_stream = state
|
||||
.response_stream
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Stream already closed".to_string())?;
|
||||
|
||||
loop {
|
||||
if let Some(newline_pos) = state.buffer.find('\n') {
|
||||
let line = state.buffer[..newline_pos].to_string();
|
||||
state.buffer = state.buffer[newline_pos + 1..].to_string();
|
||||
|
||||
if line.trim().is_empty() || line.starts_with("event:") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(event) = parse_sse_line(&line) {
|
||||
match event {
|
||||
AnthropicEvent::MessageStart { message } => {
|
||||
if let (Some(input), Some(output)) =
|
||||
(message.usage.input_tokens, message.usage.output_tokens)
|
||||
{
|
||||
return Ok(Some(LlmCompletionEvent::Usage(LlmTokenUsage {
|
||||
input_tokens: input,
|
||||
output_tokens: output,
|
||||
cache_creation_input_tokens: message
|
||||
.usage
|
||||
.cache_creation_input_tokens,
|
||||
cache_read_input_tokens: message.usage.cache_read_input_tokens,
|
||||
})));
|
||||
}
|
||||
}
|
||||
AnthropicEvent::ContentBlockStart { content_block, .. } => {
|
||||
match content_block {
|
||||
AnthropicContentBlock::Text { text } => {
|
||||
if !text.is_empty() {
|
||||
return Ok(Some(LlmCompletionEvent::Text(text)));
|
||||
}
|
||||
}
|
||||
AnthropicContentBlock::Thinking { thinking } => {
|
||||
return Ok(Some(LlmCompletionEvent::Thinking(
|
||||
LlmThinkingContent {
|
||||
text: thinking,
|
||||
signature: None,
|
||||
},
|
||||
)));
|
||||
}
|
||||
AnthropicContentBlock::RedactedThinking { data } => {
|
||||
return Ok(Some(LlmCompletionEvent::RedactedThinking(data)));
|
||||
}
|
||||
AnthropicContentBlock::ToolUse { id, name } => {
|
||||
state.current_tool_use = Some(ToolUseState {
|
||||
id,
|
||||
name,
|
||||
input_json: String::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
AnthropicEvent::ContentBlockDelta { delta, .. } => match delta {
|
||||
AnthropicDelta::TextDelta { text } => {
|
||||
if !text.is_empty() {
|
||||
return Ok(Some(LlmCompletionEvent::Text(text)));
|
||||
}
|
||||
}
|
||||
AnthropicDelta::ThinkingDelta { thinking } => {
|
||||
return Ok(Some(LlmCompletionEvent::Thinking(
|
||||
LlmThinkingContent {
|
||||
text: thinking,
|
||||
signature: None,
|
||||
},
|
||||
)));
|
||||
}
|
||||
AnthropicDelta::SignatureDelta { signature } => {
|
||||
state.pending_signature = Some(signature.clone());
|
||||
return Ok(Some(LlmCompletionEvent::Thinking(
|
||||
LlmThinkingContent {
|
||||
text: String::new(),
|
||||
signature: Some(signature),
|
||||
},
|
||||
)));
|
||||
}
|
||||
AnthropicDelta::InputJsonDelta { partial_json } => {
|
||||
if let Some(ref mut tool_use) = state.current_tool_use {
|
||||
tool_use.input_json.push_str(&partial_json);
|
||||
}
|
||||
}
|
||||
},
|
||||
AnthropicEvent::ContentBlockStop { .. } => {
|
||||
if let Some(tool_use) = state.current_tool_use.take() {
|
||||
return Ok(Some(LlmCompletionEvent::ToolUse(LlmToolUse {
|
||||
id: tool_use.id,
|
||||
name: tool_use.name,
|
||||
input: tool_use.input_json,
|
||||
thought_signature: state.pending_signature.take(),
|
||||
})));
|
||||
}
|
||||
}
|
||||
AnthropicEvent::MessageDelta { delta, usage } => {
|
||||
if let Some(reason) = delta.stop_reason {
|
||||
state.stop_reason = Some(match reason.as_str() {
|
||||
"end_turn" => LlmStopReason::EndTurn,
|
||||
"max_tokens" => LlmStopReason::MaxTokens,
|
||||
"tool_use" => LlmStopReason::ToolUse,
|
||||
_ => LlmStopReason::EndTurn,
|
||||
});
|
||||
}
|
||||
if let Some(output) = usage.output_tokens {
|
||||
return Ok(Some(LlmCompletionEvent::Usage(LlmTokenUsage {
|
||||
input_tokens: usage.input_tokens.unwrap_or(0),
|
||||
output_tokens: output,
|
||||
cache_creation_input_tokens: usage.cache_creation_input_tokens,
|
||||
cache_read_input_tokens: usage.cache_read_input_tokens,
|
||||
})));
|
||||
}
|
||||
}
|
||||
AnthropicEvent::MessageStop => {
|
||||
if let Some(stop_reason) = state.stop_reason.take() {
|
||||
return Ok(Some(LlmCompletionEvent::Stop(stop_reason)));
|
||||
}
|
||||
return Ok(Some(LlmCompletionEvent::Stop(LlmStopReason::EndTurn)));
|
||||
}
|
||||
AnthropicEvent::Ping => {}
|
||||
AnthropicEvent::Error { error } => {
|
||||
return Err(format!("API error: {}", error.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
match response_stream.next_chunk() {
|
||||
Ok(Some(chunk)) => {
|
||||
let text = String::from_utf8_lossy(&chunk);
|
||||
state.buffer.push_str(&text);
|
||||
}
|
||||
Ok(None) => {
|
||||
if let Some(stop_reason) = state.stop_reason.take() {
|
||||
return Ok(Some(LlmCompletionEvent::Stop(stop_reason)));
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Stream error: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_stream_completion_close(&mut self, stream_id: &str) {
|
||||
self.streams.lock().unwrap().remove(stream_id);
|
||||
}
|
||||
}
|
||||
|
||||
zed::register_extension!(AnthropicProvider);
|
||||
823
extensions/copilot-chat/Cargo.lock
generated
823
extensions/copilot-chat/Cargo.lock
generated
@@ -1,823 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "auditable-serde"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "copilot-chat"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"zed_extension_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auditable-serde",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"url",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad19eec017904e04c60719592a803ee5da76cb51c81e3f6fbf9457f59db49799"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "copilot-chat"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "Apache-2.0"
|
||||
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
path = "src/copilot_chat.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
zed_extension_api = { path = "../../crates/extension_api" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
@@ -1,13 +0,0 @@
|
||||
id = "copilot-chat"
|
||||
name = "Copilot Chat"
|
||||
description = "GitHub Copilot Chat LLM provider for Zed."
|
||||
version = "0.1.0"
|
||||
schema_version = 1
|
||||
authors = ["Zed Team"]
|
||||
repository = "https://github.com/zed-industries/zed"
|
||||
|
||||
[language_model_providers.copilot-chat]
|
||||
name = "Copilot Chat"
|
||||
|
||||
[language_model_providers.copilot-chat.auth]
|
||||
env_var = "GH_COPILOT_TOKEN"
|
||||
@@ -1,9 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.44643 8.76593C6.83106 8.76593 7.14286 9.0793 7.14286 9.46588V10.9825C7.14286 11.369 6.83106 11.6824 6.44643 11.6824C6.06181 11.6824 5.75 11.369 5.75 10.9825V9.46588C5.75 9.0793 6.06181 8.76593 6.44643 8.76593Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.57168 8.76593C9.95631 8.76593 10.2681 9.0793 10.2681 9.46588V10.9825C10.2681 11.369 9.95631 11.6824 9.57168 11.6824C9.18705 11.6824 8.87524 11.369 8.87524 10.9825V9.46588C8.87524 9.0793 9.18705 8.76593 9.57168 8.76593Z" fill="black"/>
|
||||
<path d="M7.99976 4.17853C7.99976 6.67853 5.83695 7.28202 4.30332 7.28202C2.76971 7.28202 2.44604 6.1547 2.44604 4.76409C2.44604 3.37347 3.68929 2.24615 5.2229 2.24615C6.75651 2.24615 7.99976 2.78791 7.99976 4.17853Z" fill="black" fill-opacity="0.5" stroke="black" stroke-width="1.2"/>
|
||||
<path d="M8 4.17853C8 6.67853 10.1628 7.28202 11.6965 7.28202C13.2301 7.28202 13.5537 6.1547 13.5537 4.76409C13.5537 3.37347 12.3105 2.24615 10.7769 2.24615C9.24325 2.24615 8 2.78791 8 4.17853Z" fill="black" fill-opacity="0.5" stroke="black" stroke-width="1.2"/>
|
||||
<path d="M12.5894 6.875C12.5894 6.875 13.3413 7.35585 13.7144 8.08398C14.0876 8.81212 14.0894 10.4985 13.7144 11.1064C13.3395 11.7143 12.8931 12.1429 11.7637 12.7543C10.6344 13.3657 9.143 13.7321 9.143 13.7321H6.85728C6.85728 13.7321 5.37513 13.4107 4.23656 12.7543C3.09798 12.0978 2.55371 11.6786 2.28585 11.1064C2.01799 10.5342 1.92871 8.85715 2.28585 8.08398C2.64299 7.31081 3.42871 6.875 3.42871 6.875" stroke="black" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path d="M11.9375 12.6016V7.33636L13.9052 7.99224V10.9255L11.9375 12.6016Z" fill="black" fill-opacity="0.75"/>
|
||||
<path d="M4.01793 12.6016V7.33636L2.05029 7.99224V10.9255L4.01793 12.6016Z" fill="black" fill-opacity="0.75"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,696 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zed_extension_api::http_client::{HttpMethod, HttpRequest, HttpResponseStream, RedirectPolicy};
|
||||
use zed_extension_api::{self as zed, *};
|
||||
|
||||
struct CopilotChatProvider {
|
||||
streams: Mutex<HashMap<String, StreamState>>,
|
||||
next_stream_id: Mutex<u64>,
|
||||
}
|
||||
|
||||
struct StreamState {
|
||||
response_stream: Option<HttpResponseStream>,
|
||||
buffer: String,
|
||||
started: bool,
|
||||
tool_calls: HashMap<usize, AccumulatedToolCall>,
|
||||
tool_calls_emitted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct AccumulatedToolCall {
|
||||
id: String,
|
||||
name: String,
|
||||
arguments: String,
|
||||
}
|
||||
|
||||
struct ModelDefinition {
|
||||
id: &'static str,
|
||||
display_name: &'static str,
|
||||
max_tokens: u64,
|
||||
max_output_tokens: Option<u64>,
|
||||
supports_images: bool,
|
||||
is_default: bool,
|
||||
is_default_fast: bool,
|
||||
}
|
||||
|
||||
const MODELS: &[ModelDefinition] = &[
|
||||
ModelDefinition {
|
||||
id: "gpt-4o",
|
||||
display_name: "GPT-4o",
|
||||
max_tokens: 128_000,
|
||||
max_output_tokens: Some(16_384),
|
||||
supports_images: true,
|
||||
is_default: true,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "gpt-4o-mini",
|
||||
display_name: "GPT-4o Mini",
|
||||
max_tokens: 128_000,
|
||||
max_output_tokens: Some(16_384),
|
||||
supports_images: true,
|
||||
is_default: false,
|
||||
is_default_fast: true,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "gpt-4.1",
|
||||
display_name: "GPT-4.1",
|
||||
max_tokens: 1_000_000,
|
||||
max_output_tokens: Some(32_768),
|
||||
supports_images: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "o1",
|
||||
display_name: "o1",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: Some(100_000),
|
||||
supports_images: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "o3-mini",
|
||||
display_name: "o3-mini",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: Some(100_000),
|
||||
supports_images: false,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "claude-3.5-sonnet",
|
||||
display_name: "Claude 3.5 Sonnet",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: Some(8_192),
|
||||
supports_images: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "claude-3.7-sonnet",
|
||||
display_name: "Claude 3.7 Sonnet",
|
||||
max_tokens: 200_000,
|
||||
max_output_tokens: Some(8_192),
|
||||
supports_images: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
id: "gemini-2.0-flash-001",
|
||||
display_name: "Gemini 2.0 Flash",
|
||||
max_tokens: 1_000_000,
|
||||
max_output_tokens: Some(8_192),
|
||||
supports_images: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
];
|
||||
|
||||
fn get_model_definition(model_id: &str) -> Option<&'static ModelDefinition> {
|
||||
MODELS.iter().find(|m| m.id == model_id)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct OpenAiRequest {
|
||||
model: String,
|
||||
messages: Vec<OpenAiMessage>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_tokens: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
tools: Vec<OpenAiTool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tool_choice: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
stop: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
temperature: Option<f32>,
|
||||
stream: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stream_options: Option<StreamOptions>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct StreamOptions {
|
||||
include_usage: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct OpenAiMessage {
|
||||
role: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
content: Option<OpenAiContent>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tool_calls: Option<Vec<OpenAiToolCall>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tool_call_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
enum OpenAiContent {
|
||||
Text(String),
|
||||
Parts(Vec<OpenAiContentPart>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(tag = "type")]
|
||||
enum OpenAiContentPart {
|
||||
#[serde(rename = "text")]
|
||||
Text { text: String },
|
||||
#[serde(rename = "image_url")]
|
||||
ImageUrl { image_url: ImageUrl },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
struct ImageUrl {
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
struct OpenAiToolCall {
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
call_type: String,
|
||||
function: OpenAiFunctionCall,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
struct OpenAiFunctionCall {
|
||||
name: String,
|
||||
arguments: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct OpenAiTool {
|
||||
#[serde(rename = "type")]
|
||||
tool_type: String,
|
||||
function: OpenAiFunctionDef,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct OpenAiFunctionDef {
|
||||
name: String,
|
||||
description: String,
|
||||
parameters: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenAiStreamResponse {
|
||||
choices: Vec<OpenAiStreamChoice>,
|
||||
#[serde(default)]
|
||||
usage: Option<OpenAiUsage>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenAiStreamChoice {
|
||||
delta: OpenAiDelta,
|
||||
finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
struct OpenAiDelta {
|
||||
#[serde(default)]
|
||||
content: Option<String>,
|
||||
#[serde(default)]
|
||||
tool_calls: Option<Vec<OpenAiToolCallDelta>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenAiToolCallDelta {
|
||||
index: usize,
|
||||
#[serde(default)]
|
||||
id: Option<String>,
|
||||
#[serde(default)]
|
||||
function: Option<OpenAiFunctionDelta>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
struct OpenAiFunctionDelta {
|
||||
#[serde(default)]
|
||||
name: Option<String>,
|
||||
#[serde(default)]
|
||||
arguments: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct OpenAiUsage {
|
||||
prompt_tokens: u64,
|
||||
completion_tokens: u64,
|
||||
}
|
||||
|
||||
fn convert_request(
|
||||
model_id: &str,
|
||||
request: &LlmCompletionRequest,
|
||||
) -> Result<OpenAiRequest, String> {
|
||||
let mut messages: Vec<OpenAiMessage> = Vec::new();
|
||||
|
||||
for msg in &request.messages {
|
||||
match msg.role {
|
||||
LlmMessageRole::System => {
|
||||
let mut text_content = String::new();
|
||||
for content in &msg.content {
|
||||
if let LlmMessageContent::Text(text) = content {
|
||||
if !text_content.is_empty() {
|
||||
text_content.push('\n');
|
||||
}
|
||||
text_content.push_str(text);
|
||||
}
|
||||
}
|
||||
if !text_content.is_empty() {
|
||||
messages.push(OpenAiMessage {
|
||||
role: "system".to_string(),
|
||||
content: Some(OpenAiContent::Text(text_content)),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
LlmMessageRole::User => {
|
||||
let mut parts: Vec<OpenAiContentPart> = Vec::new();
|
||||
let mut tool_result_messages: Vec<OpenAiMessage> = Vec::new();
|
||||
|
||||
for content in &msg.content {
|
||||
match content {
|
||||
LlmMessageContent::Text(text) => {
|
||||
if !text.is_empty() {
|
||||
parts.push(OpenAiContentPart::Text { text: text.clone() });
|
||||
}
|
||||
}
|
||||
LlmMessageContent::Image(img) => {
|
||||
let data_url = format!("data:image/png;base64,{}", img.source);
|
||||
parts.push(OpenAiContentPart::ImageUrl {
|
||||
image_url: ImageUrl { url: data_url },
|
||||
});
|
||||
}
|
||||
LlmMessageContent::ToolResult(result) => {
|
||||
let content_text = match &result.content {
|
||||
LlmToolResultContent::Text(t) => t.clone(),
|
||||
LlmToolResultContent::Image(_) => "[Image]".to_string(),
|
||||
};
|
||||
tool_result_messages.push(OpenAiMessage {
|
||||
role: "tool".to_string(),
|
||||
content: Some(OpenAiContent::Text(content_text)),
|
||||
tool_calls: None,
|
||||
tool_call_id: Some(result.tool_use_id.clone()),
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !parts.is_empty() {
|
||||
let content = if parts.len() == 1 {
|
||||
if let OpenAiContentPart::Text { text } = &parts[0] {
|
||||
OpenAiContent::Text(text.clone())
|
||||
} else {
|
||||
OpenAiContent::Parts(parts)
|
||||
}
|
||||
} else {
|
||||
OpenAiContent::Parts(parts)
|
||||
};
|
||||
|
||||
messages.push(OpenAiMessage {
|
||||
role: "user".to_string(),
|
||||
content: Some(content),
|
||||
tool_calls: None,
|
||||
tool_call_id: None,
|
||||
});
|
||||
}
|
||||
|
||||
messages.extend(tool_result_messages);
|
||||
}
|
||||
LlmMessageRole::Assistant => {
|
||||
let mut text_content = String::new();
|
||||
let mut tool_calls: Vec<OpenAiToolCall> = Vec::new();
|
||||
|
||||
for content in &msg.content {
|
||||
match content {
|
||||
LlmMessageContent::Text(text) => {
|
||||
if !text.is_empty() {
|
||||
if !text_content.is_empty() {
|
||||
text_content.push('\n');
|
||||
}
|
||||
text_content.push_str(text);
|
||||
}
|
||||
}
|
||||
LlmMessageContent::ToolUse(tool_use) => {
|
||||
tool_calls.push(OpenAiToolCall {
|
||||
id: tool_use.id.clone(),
|
||||
call_type: "function".to_string(),
|
||||
function: OpenAiFunctionCall {
|
||||
name: tool_use.name.clone(),
|
||||
arguments: tool_use.input.clone(),
|
||||
},
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
messages.push(OpenAiMessage {
|
||||
role: "assistant".to_string(),
|
||||
content: if text_content.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(OpenAiContent::Text(text_content))
|
||||
},
|
||||
tool_calls: if tool_calls.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(tool_calls)
|
||||
},
|
||||
tool_call_id: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tools: Vec<OpenAiTool> = request
|
||||
.tools
|
||||
.iter()
|
||||
.map(|t| OpenAiTool {
|
||||
tool_type: "function".to_string(),
|
||||
function: OpenAiFunctionDef {
|
||||
name: t.name.clone(),
|
||||
description: t.description.clone(),
|
||||
parameters: serde_json::from_str(&t.input_schema)
|
||||
.unwrap_or(serde_json::Value::Object(Default::default())),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
let tool_choice = request.tool_choice.as_ref().map(|tc| match tc {
|
||||
LlmToolChoice::Auto => "auto".to_string(),
|
||||
LlmToolChoice::Any => "required".to_string(),
|
||||
LlmToolChoice::None => "none".to_string(),
|
||||
});
|
||||
|
||||
let model_def = get_model_definition(model_id);
|
||||
let max_tokens = request
|
||||
.max_tokens
|
||||
.or(model_def.and_then(|m| m.max_output_tokens));
|
||||
|
||||
Ok(OpenAiRequest {
|
||||
model: model_id.to_string(),
|
||||
messages,
|
||||
max_tokens,
|
||||
tools,
|
||||
tool_choice,
|
||||
stop: request.stop_sequences.clone(),
|
||||
temperature: request.temperature,
|
||||
stream: true,
|
||||
stream_options: Some(StreamOptions {
|
||||
include_usage: true,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_sse_line(line: &str) -> Option<OpenAiStreamResponse> {
|
||||
let data = line.strip_prefix("data: ")?;
|
||||
if data.trim() == "[DONE]" {
|
||||
return None;
|
||||
}
|
||||
serde_json::from_str(data).ok()
|
||||
}
|
||||
|
||||
impl zed::Extension for CopilotChatProvider {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
streams: Mutex::new(HashMap::new()),
|
||||
next_stream_id: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_providers(&self) -> Vec<LlmProviderInfo> {
|
||||
vec![LlmProviderInfo {
|
||||
id: "copilot_chat".into(),
|
||||
name: "Copilot Chat".into(),
|
||||
icon: Some("icons/copilot.svg".into()),
|
||||
}]
|
||||
}
|
||||
|
||||
fn llm_provider_models(&self, _provider_id: &str) -> Result<Vec<LlmModelInfo>, String> {
|
||||
Ok(MODELS
|
||||
.iter()
|
||||
.map(|m| LlmModelInfo {
|
||||
id: m.id.to_string(),
|
||||
name: m.display_name.to_string(),
|
||||
max_token_count: m.max_tokens,
|
||||
max_output_tokens: m.max_output_tokens,
|
||||
capabilities: LlmModelCapabilities {
|
||||
supports_images: m.supports_images,
|
||||
supports_tools: true,
|
||||
supports_tool_choice_auto: true,
|
||||
supports_tool_choice_any: true,
|
||||
supports_tool_choice_none: true,
|
||||
supports_thinking: false,
|
||||
tool_input_format: LlmToolInputFormat::JsonSchema,
|
||||
},
|
||||
is_default: m.is_default,
|
||||
is_default_fast: m.is_default_fast,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
|
||||
llm_get_credential("copilot_chat").is_some()
|
||||
}
|
||||
|
||||
fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
|
||||
Some(
|
||||
r#"# Copilot Chat Setup
|
||||
|
||||
Welcome to **Copilot Chat**! This extension provides access to GitHub Copilot's chat models.
|
||||
|
||||
## Configuration
|
||||
|
||||
Enter your GitHub Copilot token below. You need an active GitHub Copilot subscription.
|
||||
|
||||
To get your token:
|
||||
1. Ensure you have a GitHub Copilot subscription
|
||||
2. Generate a token from your GitHub Copilot settings
|
||||
|
||||
## Available Models
|
||||
|
||||
| Model | Context | Output |
|
||||
|-------|---------|--------|
|
||||
| GPT-4o | 128K | 16K |
|
||||
| GPT-4o Mini | 128K | 16K |
|
||||
| GPT-4.1 | 1M | 32K |
|
||||
| o1 | 200K | 100K |
|
||||
| o3-mini | 200K | 100K |
|
||||
| Claude 3.5 Sonnet | 200K | 8K |
|
||||
| Claude 3.7 Sonnet | 200K | 8K |
|
||||
| Gemini 2.0 Flash | 1M | 8K |
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Full streaming support
|
||||
- ✅ Tool/function calling
|
||||
- ✅ Vision (image inputs)
|
||||
- ✅ Multiple model providers via Copilot
|
||||
|
||||
## Note
|
||||
|
||||
This extension requires an active GitHub Copilot subscription.
|
||||
"#
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
fn llm_provider_authenticate(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
let provided = llm_request_credential(
|
||||
"copilot_chat",
|
||||
LlmCredentialType::ApiKey,
|
||||
"GitHub Copilot Token",
|
||||
"ghu_...",
|
||||
)?;
|
||||
if provided {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Authentication cancelled".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_provider_reset_credentials(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
llm_delete_credential("copilot_chat")
|
||||
}
|
||||
|
||||
fn llm_stream_completion_start(
|
||||
&mut self,
|
||||
_provider_id: &str,
|
||||
model_id: &str,
|
||||
request: &LlmCompletionRequest,
|
||||
) -> Result<String, String> {
|
||||
let api_key = llm_get_credential("copilot_chat").ok_or_else(|| {
|
||||
"No token configured. Please add your GitHub Copilot token in settings.".to_string()
|
||||
})?;
|
||||
|
||||
let openai_request = convert_request(model_id, request)?;
|
||||
|
||||
let body = serde_json::to_vec(&openai_request)
|
||||
.map_err(|e| format!("Failed to serialize request: {}", e))?;
|
||||
|
||||
let http_request = HttpRequest {
|
||||
method: HttpMethod::Post,
|
||||
url: "https://api.githubcopilot.com/chat/completions".to_string(),
|
||||
headers: vec![
|
||||
("Content-Type".to_string(), "application/json".to_string()),
|
||||
("Authorization".to_string(), format!("Bearer {}", api_key)),
|
||||
(
|
||||
"Copilot-Integration-Id".to_string(),
|
||||
"vscode-chat".to_string(),
|
||||
),
|
||||
("Editor-Version".to_string(), "Zed/1.0.0".to_string()),
|
||||
],
|
||||
body: Some(body),
|
||||
redirect_policy: RedirectPolicy::FollowAll,
|
||||
};
|
||||
|
||||
let response_stream = http_request
|
||||
.fetch_stream()
|
||||
.map_err(|e| format!("HTTP request failed: {}", e))?;
|
||||
|
||||
let stream_id = {
|
||||
let mut id_counter = self.next_stream_id.lock().unwrap();
|
||||
let id = format!("copilot-stream-{}", *id_counter);
|
||||
*id_counter += 1;
|
||||
id
|
||||
};
|
||||
|
||||
self.streams.lock().unwrap().insert(
|
||||
stream_id.clone(),
|
||||
StreamState {
|
||||
response_stream: Some(response_stream),
|
||||
buffer: String::new(),
|
||||
started: false,
|
||||
tool_calls: HashMap::new(),
|
||||
tool_calls_emitted: false,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(stream_id)
|
||||
}
|
||||
|
||||
fn llm_stream_completion_next(
|
||||
&mut self,
|
||||
stream_id: &str,
|
||||
) -> Result<Option<LlmCompletionEvent>, String> {
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
let state = streams
|
||||
.get_mut(stream_id)
|
||||
.ok_or_else(|| format!("Unknown stream: {}", stream_id))?;
|
||||
|
||||
if !state.started {
|
||||
state.started = true;
|
||||
return Ok(Some(LlmCompletionEvent::Started));
|
||||
}
|
||||
|
||||
let response_stream = state
|
||||
.response_stream
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Stream already closed".to_string())?;
|
||||
|
||||
loop {
|
||||
if let Some(newline_pos) = state.buffer.find('\n') {
|
||||
let line = state.buffer[..newline_pos].to_string();
|
||||
state.buffer = state.buffer[newline_pos + 1..].to_string();
|
||||
|
||||
if line.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(response) = parse_sse_line(&line) {
|
||||
if let Some(choice) = response.choices.first() {
|
||||
if let Some(content) = &choice.delta.content {
|
||||
if !content.is_empty() {
|
||||
return Ok(Some(LlmCompletionEvent::Text(content.clone())));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tool_calls) = &choice.delta.tool_calls {
|
||||
for tc in tool_calls {
|
||||
let entry = state
|
||||
.tool_calls
|
||||
.entry(tc.index)
|
||||
.or_insert_with(AccumulatedToolCall::default);
|
||||
|
||||
if let Some(id) = &tc.id {
|
||||
entry.id = id.clone();
|
||||
}
|
||||
if let Some(func) = &tc.function {
|
||||
if let Some(name) = &func.name {
|
||||
entry.name = name.clone();
|
||||
}
|
||||
if let Some(args) = &func.arguments {
|
||||
entry.arguments.push_str(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(finish_reason) = &choice.finish_reason {
|
||||
if !state.tool_calls.is_empty() && !state.tool_calls_emitted {
|
||||
state.tool_calls_emitted = true;
|
||||
let mut tool_calls: Vec<_> = state.tool_calls.drain().collect();
|
||||
tool_calls.sort_by_key(|(idx, _)| *idx);
|
||||
|
||||
if let Some((_, tc)) = tool_calls.into_iter().next() {
|
||||
return Ok(Some(LlmCompletionEvent::ToolUse(LlmToolUse {
|
||||
id: tc.id,
|
||||
name: tc.name,
|
||||
input: tc.arguments,
|
||||
thought_signature: None,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
let stop_reason = match finish_reason.as_str() {
|
||||
"stop" => LlmStopReason::EndTurn,
|
||||
"length" => LlmStopReason::MaxTokens,
|
||||
"tool_calls" => LlmStopReason::ToolUse,
|
||||
"content_filter" => LlmStopReason::Refusal,
|
||||
_ => LlmStopReason::EndTurn,
|
||||
};
|
||||
return Ok(Some(LlmCompletionEvent::Stop(stop_reason)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(usage) = response.usage {
|
||||
return Ok(Some(LlmCompletionEvent::Usage(LlmTokenUsage {
|
||||
input_tokens: usage.prompt_tokens,
|
||||
output_tokens: usage.completion_tokens,
|
||||
cache_creation_input_tokens: None,
|
||||
cache_read_input_tokens: None,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
match response_stream.next_chunk() {
|
||||
Ok(Some(chunk)) => {
|
||||
let text = String::from_utf8_lossy(&chunk);
|
||||
state.buffer.push_str(&text);
|
||||
}
|
||||
Ok(None) => {
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Stream error: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_stream_completion_close(&mut self, stream_id: &str) {
|
||||
self.streams.lock().unwrap().remove(stream_id);
|
||||
}
|
||||
}
|
||||
|
||||
zed::register_extension!(CopilotChatProvider);
|
||||
823
extensions/google-ai/Cargo.lock
generated
823
extensions/google-ai/Cargo.lock
generated
@@ -1,823 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "auditable-serde"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foogle"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"zed_extension_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auditable-serde",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"url",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad19eec017904e04c60719592a803ee5da76cb51c81e3f6fbf9457f59db49799"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "google-ai"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "Apache-2.0"
|
||||
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
path = "src/google_ai.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
zed_extension_api = { path = "../../crates/extension_api" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
@@ -1,13 +0,0 @@
|
||||
id = "google-ai"
|
||||
name = "Google AI"
|
||||
description = "Google Gemini LLM provider for Zed."
|
||||
version = "0.1.0"
|
||||
schema_version = 1
|
||||
authors = ["Zed Team"]
|
||||
repository = "https://github.com/zed-industries/zed"
|
||||
|
||||
[language_model_providers.google-ai]
|
||||
name = "Google AI"
|
||||
|
||||
[language_model_providers.google-ai.auth]
|
||||
env_var = "GEMINI_API_KEY"
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.44 12.27C7.81333 13.1217 8 14.0317 8 15C8 14.0317 8.18083 13.1217 8.5425 12.27C8.91583 11.4183 9.4175 10.6775 10.0475 10.0475C10.6775 9.4175 11.4183 8.92167 12.27 8.56C13.1217 8.18667 14.0317 8 15 8C14.0317 8 13.1217 7.81917 12.27 7.4575C11.4411 7.1001 10.6871 6.5895 10.0475 5.9525C9.4105 5.31293 8.8999 4.55891 8.5425 3.73C8.18083 2.87833 8 1.96833 8 1C8 1.96833 7.81333 2.87833 7.44 3.73C7.07833 4.58167 6.5825 5.3225 5.9525 5.9525C5.31293 6.5895 4.55891 7.1001 3.73 7.4575C2.87833 7.81917 1.96833 8 1 8C1.96833 8 2.87833 8.18667 3.73 8.56C4.58167 8.92167 5.3225 9.4175 5.9525 10.0475C6.5825 10.6775 7.07833 11.4183 7.44 12.27Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 762 B |
@@ -1,840 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zed_extension_api::http_client::{HttpMethod, HttpRequest, HttpResponseStream, RedirectPolicy};
|
||||
use zed_extension_api::{self as zed, *};
|
||||
|
||||
static TOOL_CALL_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
struct GoogleAiProvider {
|
||||
streams: Mutex<HashMap<String, StreamState>>,
|
||||
next_stream_id: Mutex<u64>,
|
||||
}
|
||||
|
||||
struct StreamState {
|
||||
response_stream: Option<HttpResponseStream>,
|
||||
buffer: String,
|
||||
started: bool,
|
||||
stop_reason: Option<LlmStopReason>,
|
||||
wants_tool_use: bool,
|
||||
}
|
||||
|
||||
struct ModelDefinition {
|
||||
real_id: &'static str,
|
||||
display_name: &'static str,
|
||||
max_tokens: u64,
|
||||
max_output_tokens: Option<u64>,
|
||||
supports_images: bool,
|
||||
supports_thinking: bool,
|
||||
is_default: bool,
|
||||
is_default_fast: bool,
|
||||
}
|
||||
|
||||
const MODELS: &[ModelDefinition] = &[
|
||||
ModelDefinition {
|
||||
real_id: "gemini-2.5-flash-lite",
|
||||
display_name: "Gemini 2.5 Flash-Lite",
|
||||
max_tokens: 1_048_576,
|
||||
max_output_tokens: Some(65_536),
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: true,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "gemini-2.5-flash",
|
||||
display_name: "Gemini 2.5 Flash",
|
||||
max_tokens: 1_048_576,
|
||||
max_output_tokens: Some(65_536),
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: true,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "gemini-2.5-pro",
|
||||
display_name: "Gemini 2.5 Pro",
|
||||
max_tokens: 1_048_576,
|
||||
max_output_tokens: Some(65_536),
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
ModelDefinition {
|
||||
real_id: "gemini-3-pro-preview",
|
||||
display_name: "Gemini 3 Pro",
|
||||
max_tokens: 1_048_576,
|
||||
max_output_tokens: Some(65_536),
|
||||
supports_images: true,
|
||||
supports_thinking: true,
|
||||
is_default: false,
|
||||
is_default_fast: false,
|
||||
},
|
||||
];
|
||||
|
||||
fn get_real_model_id(display_name: &str) -> Option<&'static str> {
|
||||
MODELS
|
||||
.iter()
|
||||
.find(|m| m.display_name == display_name)
|
||||
.map(|m| m.real_id)
|
||||
}
|
||||
|
||||
fn get_model_supports_thinking(display_name: &str) -> bool {
|
||||
MODELS
|
||||
.iter()
|
||||
.find(|m| m.display_name == display_name)
|
||||
.map(|m| m.supports_thinking)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Adapts a JSON schema to be compatible with Google's API subset.
|
||||
/// Google only supports a specific subset of JSON Schema fields.
|
||||
/// See: https://ai.google.dev/api/caching#Schema
|
||||
fn adapt_schema_for_google(json: &mut serde_json::Value) {
|
||||
adapt_schema_for_google_impl(json, true);
|
||||
}
|
||||
|
||||
fn adapt_schema_for_google_impl(json: &mut serde_json::Value, is_schema: bool) {
|
||||
if let serde_json::Value::Object(obj) = json {
|
||||
// Google's Schema only supports these fields:
|
||||
// type, format, title, description, nullable, enum, maxItems, minItems,
|
||||
// properties, required, minProperties, maxProperties, minLength, maxLength,
|
||||
// pattern, example, anyOf, propertyOrdering, default, items, minimum, maximum
|
||||
const ALLOWED_KEYS: &[&str] = &[
|
||||
"type",
|
||||
"format",
|
||||
"title",
|
||||
"description",
|
||||
"nullable",
|
||||
"enum",
|
||||
"maxItems",
|
||||
"minItems",
|
||||
"properties",
|
||||
"required",
|
||||
"minProperties",
|
||||
"maxProperties",
|
||||
"minLength",
|
||||
"maxLength",
|
||||
"pattern",
|
||||
"example",
|
||||
"anyOf",
|
||||
"propertyOrdering",
|
||||
"default",
|
||||
"items",
|
||||
"minimum",
|
||||
"maximum",
|
||||
];
|
||||
|
||||
// Convert oneOf to anyOf before filtering keys
|
||||
if let Some(one_of) = obj.remove("oneOf") {
|
||||
obj.insert("anyOf".to_string(), one_of);
|
||||
}
|
||||
|
||||
// If type is an array (e.g., ["string", "null"]), take just the first type
|
||||
if let Some(type_field) = obj.get_mut("type") {
|
||||
if let serde_json::Value::Array(types) = type_field {
|
||||
if let Some(first_type) = types.first().cloned() {
|
||||
*type_field = first_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only filter keys if this is a schema object, not a properties map
|
||||
if is_schema {
|
||||
obj.retain(|key, _| ALLOWED_KEYS.contains(&key.as_str()));
|
||||
}
|
||||
|
||||
// Recursively process nested values
|
||||
// "properties" contains a map of property names -> schemas
|
||||
// "items" and "anyOf" contain schemas directly
|
||||
for (key, value) in obj.iter_mut() {
|
||||
if key == "properties" {
|
||||
// properties is a map of property_name -> schema
|
||||
if let serde_json::Value::Object(props) = value {
|
||||
for (_, prop_schema) in props.iter_mut() {
|
||||
adapt_schema_for_google_impl(prop_schema, true);
|
||||
}
|
||||
}
|
||||
} else if key == "items" {
|
||||
// items is a schema
|
||||
adapt_schema_for_google_impl(value, true);
|
||||
} else if key == "anyOf" {
|
||||
// anyOf is an array of schemas
|
||||
if let serde_json::Value::Array(arr) = value {
|
||||
for item in arr.iter_mut() {
|
||||
adapt_schema_for_google_impl(item, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let serde_json::Value::Array(arr) = json {
|
||||
for item in arr.iter_mut() {
|
||||
adapt_schema_for_google_impl(item, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleRequest {
|
||||
contents: Vec<GoogleContent>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
system_instruction: Option<GoogleSystemInstruction>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
generation_config: Option<GoogleGenerationConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tools: Option<Vec<GoogleTool>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tool_config: Option<GoogleToolConfig>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleSystemInstruction {
|
||||
parts: Vec<GooglePart>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleContent {
|
||||
parts: Vec<GooglePart>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
role: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
enum GooglePart {
|
||||
Text(GoogleTextPart),
|
||||
InlineData(GoogleInlineDataPart),
|
||||
FunctionCall(GoogleFunctionCallPart),
|
||||
FunctionResponse(GoogleFunctionResponsePart),
|
||||
Thought(GoogleThoughtPart),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleTextPart {
|
||||
text: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleInlineDataPart {
|
||||
inline_data: GoogleBlob,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleBlob {
|
||||
mime_type: String,
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleFunctionCallPart {
|
||||
function_call: GoogleFunctionCall,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
thought_signature: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleFunctionCall {
|
||||
name: String,
|
||||
args: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleFunctionResponsePart {
|
||||
function_response: GoogleFunctionResponse,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleFunctionResponse {
|
||||
name: String,
|
||||
response: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleThoughtPart {
|
||||
thought: bool,
|
||||
thought_signature: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleGenerationConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
candidate_count: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stop_sequences: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_output_tokens: Option<usize>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
temperature: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
thinking_config: Option<GoogleThinkingConfig>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleThinkingConfig {
|
||||
thinking_budget: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleTool {
|
||||
function_declarations: Vec<GoogleFunctionDeclaration>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleFunctionDeclaration {
|
||||
name: String,
|
||||
description: String,
|
||||
parameters: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleToolConfig {
|
||||
function_calling_config: GoogleFunctionCallingConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleFunctionCallingConfig {
|
||||
mode: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
allowed_function_names: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleStreamResponse {
|
||||
#[serde(default)]
|
||||
candidates: Vec<GoogleCandidate>,
|
||||
#[serde(default)]
|
||||
usage_metadata: Option<GoogleUsageMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleCandidate {
|
||||
#[serde(default)]
|
||||
content: Option<GoogleContent>,
|
||||
#[serde(default)]
|
||||
finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GoogleUsageMetadata {
|
||||
#[serde(default)]
|
||||
prompt_token_count: u64,
|
||||
#[serde(default)]
|
||||
candidates_token_count: u64,
|
||||
}
|
||||
|
||||
fn convert_request(
|
||||
model_id: &str,
|
||||
request: &LlmCompletionRequest,
|
||||
) -> Result<(GoogleRequest, String), String> {
|
||||
let real_model_id =
|
||||
get_real_model_id(model_id).ok_or_else(|| format!("Unknown model: {}", model_id))?;
|
||||
|
||||
let supports_thinking = get_model_supports_thinking(model_id);
|
||||
|
||||
let mut contents: Vec<GoogleContent> = Vec::new();
|
||||
let mut system_parts: Vec<GooglePart> = Vec::new();
|
||||
|
||||
for msg in &request.messages {
|
||||
match msg.role {
|
||||
LlmMessageRole::System => {
|
||||
for content in &msg.content {
|
||||
if let LlmMessageContent::Text(text) = content {
|
||||
if !text.is_empty() {
|
||||
system_parts
|
||||
.push(GooglePart::Text(GoogleTextPart { text: text.clone() }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LlmMessageRole::User => {
|
||||
let mut parts: Vec<GooglePart> = Vec::new();
|
||||
|
||||
for content in &msg.content {
|
||||
match content {
|
||||
LlmMessageContent::Text(text) => {
|
||||
if !text.is_empty() {
|
||||
parts.push(GooglePart::Text(GoogleTextPart { text: text.clone() }));
|
||||
}
|
||||
}
|
||||
LlmMessageContent::Image(img) => {
|
||||
parts.push(GooglePart::InlineData(GoogleInlineDataPart {
|
||||
inline_data: GoogleBlob {
|
||||
mime_type: "image/png".to_string(),
|
||||
data: img.source.clone(),
|
||||
},
|
||||
}));
|
||||
}
|
||||
LlmMessageContent::ToolResult(result) => {
|
||||
let response_value = match &result.content {
|
||||
LlmToolResultContent::Text(t) => {
|
||||
serde_json::json!({ "output": t })
|
||||
}
|
||||
LlmToolResultContent::Image(_) => {
|
||||
serde_json::json!({ "output": "Tool responded with an image" })
|
||||
}
|
||||
};
|
||||
parts.push(GooglePart::FunctionResponse(GoogleFunctionResponsePart {
|
||||
function_response: GoogleFunctionResponse {
|
||||
name: result.tool_name.clone(),
|
||||
response: response_value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !parts.is_empty() {
|
||||
contents.push(GoogleContent {
|
||||
parts,
|
||||
role: Some("user".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
LlmMessageRole::Assistant => {
|
||||
let mut parts: Vec<GooglePart> = Vec::new();
|
||||
|
||||
for content in &msg.content {
|
||||
match content {
|
||||
LlmMessageContent::Text(text) => {
|
||||
if !text.is_empty() {
|
||||
parts.push(GooglePart::Text(GoogleTextPart { text: text.clone() }));
|
||||
}
|
||||
}
|
||||
LlmMessageContent::ToolUse(tool_use) => {
|
||||
let thought_signature =
|
||||
tool_use.thought_signature.clone().filter(|s| !s.is_empty());
|
||||
|
||||
let args: serde_json::Value =
|
||||
serde_json::from_str(&tool_use.input).unwrap_or_default();
|
||||
|
||||
parts.push(GooglePart::FunctionCall(GoogleFunctionCallPart {
|
||||
function_call: GoogleFunctionCall {
|
||||
name: tool_use.name.clone(),
|
||||
args,
|
||||
},
|
||||
thought_signature,
|
||||
}));
|
||||
}
|
||||
LlmMessageContent::Thinking(thinking) => {
|
||||
if let Some(ref signature) = thinking.signature {
|
||||
if !signature.is_empty() {
|
||||
parts.push(GooglePart::Thought(GoogleThoughtPart {
|
||||
thought: true,
|
||||
thought_signature: signature.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !parts.is_empty() {
|
||||
contents.push(GoogleContent {
|
||||
parts,
|
||||
role: Some("model".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let system_instruction = if system_parts.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GoogleSystemInstruction {
|
||||
parts: system_parts,
|
||||
})
|
||||
};
|
||||
|
||||
let tools: Option<Vec<GoogleTool>> = if request.tools.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let declarations: Vec<GoogleFunctionDeclaration> = request
|
||||
.tools
|
||||
.iter()
|
||||
.map(|t| {
|
||||
let mut parameters: serde_json::Value = serde_json::from_str(&t.input_schema)
|
||||
.unwrap_or(serde_json::Value::Object(Default::default()));
|
||||
adapt_schema_for_google(&mut parameters);
|
||||
GoogleFunctionDeclaration {
|
||||
name: t.name.clone(),
|
||||
description: t.description.clone(),
|
||||
parameters,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(vec![GoogleTool {
|
||||
function_declarations: declarations,
|
||||
}])
|
||||
};
|
||||
|
||||
let tool_config = request.tool_choice.as_ref().map(|tc| {
|
||||
let mode = match tc {
|
||||
LlmToolChoice::Auto => "AUTO",
|
||||
LlmToolChoice::Any => "ANY",
|
||||
LlmToolChoice::None => "NONE",
|
||||
};
|
||||
GoogleToolConfig {
|
||||
function_calling_config: GoogleFunctionCallingConfig {
|
||||
mode: mode.to_string(),
|
||||
allowed_function_names: None,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let thinking_config = if supports_thinking && request.thinking_allowed {
|
||||
Some(GoogleThinkingConfig {
|
||||
thinking_budget: 8192,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let generation_config = Some(GoogleGenerationConfig {
|
||||
candidate_count: Some(1),
|
||||
stop_sequences: if request.stop_sequences.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(request.stop_sequences.clone())
|
||||
},
|
||||
max_output_tokens: None,
|
||||
temperature: request.temperature.map(|t| t as f64).or(Some(1.0)),
|
||||
thinking_config,
|
||||
});
|
||||
|
||||
Ok((
|
||||
GoogleRequest {
|
||||
contents,
|
||||
system_instruction,
|
||||
generation_config,
|
||||
tools,
|
||||
tool_config,
|
||||
},
|
||||
real_model_id.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_stream_line(line: &str) -> Option<GoogleStreamResponse> {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() || trimmed == "[" || trimmed == "]" || trimmed == "," {
|
||||
return None;
|
||||
}
|
||||
|
||||
let json_str = trimmed.strip_prefix("data: ").unwrap_or(trimmed);
|
||||
let json_str = json_str.trim_start_matches(',').trim();
|
||||
|
||||
if json_str.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
serde_json::from_str(json_str).ok()
|
||||
}
|
||||
|
||||
impl zed::Extension for GoogleAiProvider {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
streams: Mutex::new(HashMap::new()),
|
||||
next_stream_id: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_providers(&self) -> Vec<LlmProviderInfo> {
|
||||
vec![LlmProviderInfo {
|
||||
id: "google-ai".into(),
|
||||
name: "Google AI".into(),
|
||||
icon: Some("icons/google-ai.svg".into()),
|
||||
}]
|
||||
}
|
||||
|
||||
fn llm_provider_models(&self, _provider_id: &str) -> Result<Vec<LlmModelInfo>, String> {
|
||||
Ok(MODELS
|
||||
.iter()
|
||||
.map(|m| LlmModelInfo {
|
||||
id: m.display_name.to_string(),
|
||||
name: m.display_name.to_string(),
|
||||
max_token_count: m.max_tokens,
|
||||
max_output_tokens: m.max_output_tokens,
|
||||
capabilities: LlmModelCapabilities {
|
||||
supports_images: m.supports_images,
|
||||
supports_tools: true,
|
||||
supports_tool_choice_auto: true,
|
||||
supports_tool_choice_any: true,
|
||||
supports_tool_choice_none: true,
|
||||
supports_thinking: m.supports_thinking,
|
||||
tool_input_format: LlmToolInputFormat::JsonSchema,
|
||||
},
|
||||
is_default: m.is_default,
|
||||
is_default_fast: m.is_default_fast,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
|
||||
llm_get_credential("google-ai").is_some()
|
||||
}
|
||||
|
||||
fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
|
||||
Some(
|
||||
r#"# Google AI Setup
|
||||
|
||||
Welcome to **Google AI**! This extension provides access to Google Gemini models.
|
||||
|
||||
## Configuration
|
||||
|
||||
Enter your Google AI API key below. You can get your API key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey).
|
||||
|
||||
## Available Models
|
||||
|
||||
| Display Name | Real Model | Context | Output |
|
||||
|--------------|------------|---------|--------|
|
||||
| Gemini 2.5 Flash-Lite | gemini-2.5-flash-lite | 1M | 65K |
|
||||
| Gemini 2.5 Flash | gemini-2.5-flash | 1M | 65K |
|
||||
| Gemini 2.5 Pro | gemini-2.5-pro | 1M | 65K |
|
||||
| Gemini 3 Pro | gemini-3-pro-preview | 1M | 65K |
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Full streaming support
|
||||
- ✅ Tool/function calling with thought signatures
|
||||
- ✅ Vision (image inputs)
|
||||
- ✅ Extended thinking support
|
||||
- ✅ All Gemini models
|
||||
|
||||
## Pricing
|
||||
|
||||
Uses your Google AI API credits. See [Google AI pricing](https://ai.google.dev/pricing) for details.
|
||||
"#
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
fn llm_provider_authenticate(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
let provided = llm_request_credential(
|
||||
"google-ai",
|
||||
LlmCredentialType::ApiKey,
|
||||
"Google AI API Key",
|
||||
"AIza...",
|
||||
)?;
|
||||
if provided {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Authentication cancelled".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_provider_reset_credentials(&mut self, _provider_id: &str) -> Result<(), String> {
|
||||
llm_delete_credential("google-ai")
|
||||
}
|
||||
|
||||
fn llm_stream_completion_start(
|
||||
&mut self,
|
||||
_provider_id: &str,
|
||||
model_id: &str,
|
||||
request: &LlmCompletionRequest,
|
||||
) -> Result<String, String> {
|
||||
let api_key = llm_get_credential("google-ai").ok_or_else(|| {
|
||||
"No API key configured. Please add your Google AI API key in settings.".to_string()
|
||||
})?;
|
||||
|
||||
let (google_request, real_model_id) = convert_request(model_id, request)?;
|
||||
|
||||
let body = serde_json::to_vec(&google_request)
|
||||
.map_err(|e| format!("Failed to serialize request: {}", e))?;
|
||||
|
||||
let url = format!(
|
||||
"https://generativelanguage.googleapis.com/v1beta/models/{}:streamGenerateContent?alt=sse&key={}",
|
||||
real_model_id, api_key
|
||||
);
|
||||
|
||||
let http_request = HttpRequest {
|
||||
method: HttpMethod::Post,
|
||||
url,
|
||||
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
|
||||
body: Some(body),
|
||||
redirect_policy: RedirectPolicy::FollowAll,
|
||||
};
|
||||
|
||||
let response_stream = http_request
|
||||
.fetch_stream()
|
||||
.map_err(|e| format!("HTTP request failed: {}", e))?;
|
||||
|
||||
let stream_id = {
|
||||
let mut id_counter = self.next_stream_id.lock().unwrap();
|
||||
let id = format!("google-ai-stream-{}", *id_counter);
|
||||
*id_counter += 1;
|
||||
id
|
||||
};
|
||||
|
||||
self.streams.lock().unwrap().insert(
|
||||
stream_id.clone(),
|
||||
StreamState {
|
||||
response_stream: Some(response_stream),
|
||||
buffer: String::new(),
|
||||
started: false,
|
||||
stop_reason: None,
|
||||
wants_tool_use: false,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(stream_id)
|
||||
}
|
||||
|
||||
fn llm_stream_completion_next(
|
||||
&mut self,
|
||||
stream_id: &str,
|
||||
) -> Result<Option<LlmCompletionEvent>, String> {
|
||||
let mut streams = self.streams.lock().unwrap();
|
||||
let state = streams
|
||||
.get_mut(stream_id)
|
||||
.ok_or_else(|| format!("Unknown stream: {}", stream_id))?;
|
||||
|
||||
if !state.started {
|
||||
state.started = true;
|
||||
return Ok(Some(LlmCompletionEvent::Started));
|
||||
}
|
||||
|
||||
let response_stream = state
|
||||
.response_stream
|
||||
.as_mut()
|
||||
.ok_or_else(|| "Stream already closed".to_string())?;
|
||||
|
||||
loop {
|
||||
if let Some(newline_pos) = state.buffer.find('\n') {
|
||||
let line = state.buffer[..newline_pos].to_string();
|
||||
state.buffer = state.buffer[newline_pos + 1..].to_string();
|
||||
|
||||
if let Some(response) = parse_stream_line(&line) {
|
||||
for candidate in response.candidates {
|
||||
if let Some(finish_reason) = &candidate.finish_reason {
|
||||
state.stop_reason = Some(match finish_reason.as_str() {
|
||||
"STOP" => {
|
||||
if state.wants_tool_use {
|
||||
LlmStopReason::ToolUse
|
||||
} else {
|
||||
LlmStopReason::EndTurn
|
||||
}
|
||||
}
|
||||
"MAX_TOKENS" => LlmStopReason::MaxTokens,
|
||||
"SAFETY" => LlmStopReason::Refusal,
|
||||
_ => LlmStopReason::EndTurn,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(content) = candidate.content {
|
||||
for part in content.parts {
|
||||
match part {
|
||||
GooglePart::Text(text_part) => {
|
||||
if !text_part.text.is_empty() {
|
||||
return Ok(Some(LlmCompletionEvent::Text(
|
||||
text_part.text,
|
||||
)));
|
||||
}
|
||||
}
|
||||
GooglePart::FunctionCall(fc_part) => {
|
||||
state.wants_tool_use = true;
|
||||
let next_tool_id =
|
||||
TOOL_CALL_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
let id = format!(
|
||||
"{}-{}",
|
||||
fc_part.function_call.name, next_tool_id
|
||||
);
|
||||
|
||||
let thought_signature =
|
||||
fc_part.thought_signature.filter(|s| !s.is_empty());
|
||||
|
||||
return Ok(Some(LlmCompletionEvent::ToolUse(LlmToolUse {
|
||||
id,
|
||||
name: fc_part.function_call.name,
|
||||
input: fc_part.function_call.args.to_string(),
|
||||
thought_signature,
|
||||
})));
|
||||
}
|
||||
GooglePart::Thought(thought_part) => {
|
||||
return Ok(Some(LlmCompletionEvent::Thinking(
|
||||
LlmThinkingContent {
|
||||
text: "(Encrypted thought)".to_string(),
|
||||
signature: Some(thought_part.thought_signature),
|
||||
},
|
||||
)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(usage) = response.usage_metadata {
|
||||
return Ok(Some(LlmCompletionEvent::Usage(LlmTokenUsage {
|
||||
input_tokens: usage.prompt_token_count,
|
||||
output_tokens: usage.candidates_token_count,
|
||||
cache_creation_input_tokens: None,
|
||||
cache_read_input_tokens: None,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
match response_stream.next_chunk() {
|
||||
Ok(Some(chunk)) => {
|
||||
let text = String::from_utf8_lossy(&chunk);
|
||||
state.buffer.push_str(&text);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Stream ended - check if we have a stop reason
|
||||
if let Some(stop_reason) = state.stop_reason.take() {
|
||||
return Ok(Some(LlmCompletionEvent::Stop(stop_reason)));
|
||||
}
|
||||
|
||||
// No stop reason - this is unexpected. Check if buffer contains error info
|
||||
let mut error_msg = String::from("Stream ended unexpectedly.");
|
||||
|
||||
// Try to parse remaining buffer as potential error response
|
||||
if !state.buffer.is_empty() {
|
||||
error_msg.push_str(&format!(
|
||||
"\nRemaining buffer: {}",
|
||||
&state.buffer[..state.buffer.len().min(1000)]
|
||||
));
|
||||
}
|
||||
|
||||
return Err(error_msg);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Stream error: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_stream_completion_close(&mut self, stream_id: &str) {
|
||||
self.streams.lock().unwrap().remove(stream_id);
|
||||
}
|
||||
}
|
||||
|
||||
zed::register_extension!(GoogleAiProvider);
|
||||
823
extensions/open-router/Cargo.lock
generated
823
extensions/open-router/Cargo.lock
generated
@@ -1,823 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "auditable-serde"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "open_router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"zed_extension_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auditable-serde",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"url",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad19eec017904e04c60719592a803ee5da76cb51c81e3f6fbf9457f59db49799"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_extension_api"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user