Compare commits

..

3 Commits

Author SHA1 Message Date
Richard Feldman
9404251dd0 Tweak workflow prompt to be more explicit about step boundaries 2024-08-16 16:41:15 -04:00
Richard Feldman
289ba8fb34 Make find_most_similar resilient to queries that contain types 2024-08-16 16:14:53 -04:00
Richard Feldman
ba7e894a6e Simplify workflow prompt 2024-08-16 15:41:47 -04:00
17 changed files with 142 additions and 486 deletions

View File

@@ -1,12 +1,11 @@
<workflow>
Guide the user through code changes in numbered steps that focus on individual functions, type definitions, etc.
Guide the user through code changes in numbered steps where each step focuses on a single individual function, type definition, etc.
Surround each distinct step in a <step></step> XML tag. The user will be performing these steps in a code editor
named Zed, which is where they will have entered this prompt and will be seeing the response.
<instructions>
- Use the language of the file for code fence blocks unless otherwise specified.
- Include a code or file action in each step.
- Only put code in separate steps if it should either go in separate files, or in different (non-contiguous) places in the same file.
- Provide error handling and input validation where appropriate.
- Adapt explanations based on the user's perceived level of expertise.
- Include comments in code examples to enhance understanding.

View File

@@ -1,79 +0,0 @@
You are an assistant in a code editor, helping a developer to edit their code.
<task>
Output {{ content_type }} to insert into the given `<document>` in accordance with the given `<prompt>` tag.
Generate output to be inserted prior to the special ⎀ character.
</task>
<rule>
You are only to insert NEW {{{ content_type }}} based on the specified `<prompt>` tag.
DO NOT repeat any content that surrounds the ⎀ character
DO NOT repeat the ⎀ character itself.
<rule_example>
<example_input>
<document language="Rust">
impl Foo {
fn bar() {}
}
</document>
<prompt>Document method</prompt>
</example_input>
<bad_output failure="Not a pure insert. Repeated content surrounding ⎀">
// Example method
// This is an example of a doc commment
// It could be multiple lines
fn bar() {}
</bad_output>
<bad_output failure="Repeated ⎀ in output">
// Example method
// This is an example of a doc commment
// It could be multiple lines⎀
</bad_output>
<good_output success="Generated ONLY new code based on the prompt tag">
// Example method
// This is an example of a doc commment
// It could be multiple lines
</good_output>
</rule_example>
</rule>
{{#if truncated}}
<document language="{{{ language }}}" truncated="true">
{{else}}
<document language="{{{ language }}}">
{{/if}}
{{{ document_prefix }}}{{{ document_suffix }}}
</document>
{{#if truncated}}
<note>
The contents of the document tag above have been truncated for brevity.
</note>
{{/if}}
<instruction>
Focus on inserting {{{ content_type }}} at the location prior to the ⎀ character based on the `<prompt>` below.
</instruction>
Here's the excerpt from the document where you will perform the edit.
<document_excerpt>
{{{ context_prefix }}}{{{ context_suffix }}}
</document_excerpt>
<prompt>
{{{ prompt }}}
</prompt>
<rules>
- Your first token should be {{{ content_type }}} written in {{{ language }}}.
- DO NOT repeat existing {{{ content_type }}}.
- DO NOT output the ⎀ character.
- DO NOT add extra indentation to the generated content.
</rules>
<output_format>
Output pure {{{ language }}} with no commentary.
</output_format>

View File

@@ -1,73 +0,0 @@
You are an assistant in a code editor, helping a developer to edit their code.
<task>
Output code according to the given `<prompt>` tag, which should be inserted into the given `<document>` tag replacing the contents of the `<replaced_text>` tag.
</task>
<instructions>
* Maintain the original indentation level of the file in rewritten sections.
</instructions>
<document language="Python">
import pygame
import random
# Initialize Pygame
pygame.init()
# Set up the game window
width = 800
height = 600
window = pygame.display.set_mode((width, height))
pygame.display.set_caption("Snake Game")
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# Snake properties
snake_block = 20
snake_speed = 15
# Initialize the snake
snake = [(width // 2, height // 2)]
snake_direction = (0, -snake_block)
# Initialize the food
food = (random.randrange(0, width - snake_block, snake_block),
random.randrange(0, height - snake_block, snake_block))
# Set up the game clock
clock = pygame.time.Clock()
# Game loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if <replaced_text>event.key == pygame.K_UP and snake_direction != (0, snake_block):
snake_direction = (0, -snake_block)
elif event.key == pygame.K_DOWN and snake_direction != (0, -snake_block):</replaced_text>
snake_direction = (0, snake_block)
elif event.key == pygame.K_LEFT and snake_direction != (snake_block, 0):
snake_direction = (-snake_block, 0)
elif event.key == pygame.K_RIGHT and snake_direction != (-snake_block, 0):
snake_direction = (snake_block, 0)</replaced_text>
# Move the snake
new_head = (snake[0][0] + snake_direction[0], snake[0][1] +
</document>
<prompt>
Apply demorgans
</prompt>
<directive>
Output code immediately. No commentary. Your first token should be in Python.
</directive>
event.key == pygame.K_UP and not snake_direction == (0, snake_block):
snake_direction = (0, -snake_block)
elif event.key == pygame.K_DOWN and not snake_direction == (0, -snake_block):

View File

@@ -38,8 +38,6 @@ pub enum Model {
Custom {
name: String,
max_tokens: usize,
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
display_name: Option<String>,
/// Override this model with a different Anthropic model for tool calls.
tool_override: Option<String>,
/// Indicates whether this custom model supports caching.
@@ -79,9 +77,7 @@ impl Model {
Self::Claude3Opus => "Claude 3 Opus",
Self::Claude3Sonnet => "Claude 3 Sonnet",
Self::Claude3Haiku => "Claude 3 Haiku",
Self::Custom {
name, display_name, ..
} => display_name.as_ref().unwrap_or(name),
Self::Custom { name, .. } => name,
}
}

View File

@@ -3506,8 +3506,7 @@ impl ContextEditor {
};
Some(
h_flex()
.px_3()
.py_2()
.p_3()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
@@ -3656,8 +3655,8 @@ impl Render for ContextEditor {
this.child(
div()
.absolute()
.right_3()
.bottom_12()
.right_4()
.bottom_10()
.max_w_96()
.py_2()
.px_3()
@@ -3671,8 +3670,8 @@ impl Render for ContextEditor {
this.child(
div()
.absolute()
.right_3()
.bottom_12()
.right_4()
.bottom_10()
.max_w_96()
.py_2()
.px_3()
@@ -3683,12 +3682,12 @@ impl Render for ContextEditor {
.gap_0p5()
.child(
h_flex()
.gap_1p5()
.gap_1()
.items_center()
.child(Icon::new(IconName::XCircle).color(Color::Error))
.child(
Label::new("Error interacting with language model")
.weight(FontWeight::MEDIUM),
.weight(FontWeight::SEMIBOLD),
),
)
.child(

View File

@@ -28,7 +28,7 @@ use gpui::{
FontWeight, Global, HighlightStyle, Model, ModelContext, Subscription, Task, TextStyle,
UpdateGlobal, View, ViewContext, WeakView, WindowContext,
};
use language::{Buffer, Point, TransactionId};
use language::{Buffer, IndentKind, Point, TransactionId};
use language_model::{
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
};
@@ -46,6 +46,7 @@ use std::{
task::{self, Poll},
time::{Duration, Instant},
};
use text::OffsetRangeExt as _;
use theme::ThemeSettings;
use ui::{prelude::*, CheckboxWithLabel, IconButtonShape, Popover, Tooltip};
use util::{RangeExt, ResultExt};
@@ -72,7 +73,6 @@ const PROMPT_HISTORY_MAX_LEN: usize = 20;
pub struct InlineAssistant {
next_assist_id: InlineAssistId,
next_assist_group_id: InlineAssistGroupId,
assists: HashMap<InlineAssistId, InlineAssist>,
assists_by_editor: HashMap<WeakView<Editor>, EditorInlineAssists>,
assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>,
@@ -154,51 +154,40 @@ impl InlineAssistant {
.map(|range| range.to_point(&snapshot))
.collect::<Vec<Range<Point>>>();
if selection_ranges.len() == 1 {
let selection_range = &selection_ranges[0];
codegen_ranges.push(CodegenRange {
transform_range: selection_range.clone(),
selection_ranges: vec![selection_range.clone()],
focus_assist: true,
});
} else {
for selection_range in selection_ranges {
let selection_is_newest =
newest_selection_range.contains_inclusive(&selection_range);
let mut transform_range = selection_range.start..selection_range.end;
for selection_range in selection_ranges {
let selection_is_newest = newest_selection_range.contains_inclusive(&selection_range);
let mut transform_range = selection_range.start..selection_range.end;
// Expand the transform range to start/end of lines.
// If a non-empty selection ends at the start of the last line, clip at the end of the penultimate line.
transform_range.start.column = 0;
if transform_range.end.column == 0 && transform_range.end > transform_range.start {
transform_range.end.row -= 1;
}
transform_range.end.column =
snapshot.line_len(MultiBufferRow(transform_range.end.row));
let selection_range =
selection_range.start..selection_range.end.min(transform_range.end);
// If we intersect the previous transform range,
if let Some(CodegenRange {
transform_range: prev_transform_range,
selection_ranges,
focus_assist,
}) = codegen_ranges.last_mut()
{
if transform_range.start <= prev_transform_range.end {
prev_transform_range.end = transform_range.end;
selection_ranges.push(selection_range);
*focus_assist |= selection_is_newest;
continue;
}
}
codegen_ranges.push(CodegenRange {
transform_range,
selection_ranges: vec![selection_range],
focus_assist: selection_is_newest,
})
// Expand the transform range to start/end of lines.
// If a non-empty selection ends at the start of the last line, clip at the end of the penultimate line.
transform_range.start.column = 0;
if transform_range.end.column == 0 && transform_range.end > transform_range.start {
transform_range.end.row -= 1;
}
transform_range.end.column = snapshot.line_len(MultiBufferRow(transform_range.end.row));
let selection_range =
selection_range.start..selection_range.end.min(transform_range.end);
// If we intersect the previous transform range,
if let Some(CodegenRange {
transform_range: prev_transform_range,
selection_ranges,
focus_assist,
}) = codegen_ranges.last_mut()
{
if transform_range.start <= prev_transform_range.end {
prev_transform_range.end = transform_range.end;
selection_ranges.push(selection_range);
*focus_assist |= selection_is_newest;
continue;
}
}
codegen_ranges.push(CodegenRange {
transform_range,
selection_ranges: vec![selection_range],
focus_assist: selection_is_newest,
})
}
let assist_group_id = self.next_assist_group_id.post_inc();
@@ -1089,10 +1078,10 @@ impl InlineAssistant {
let mut new_blocks = Vec::new();
for (new_row, old_row_range) in deleted_row_ranges {
let (_, buffer_start) = old_snapshot
.position_to_buffer_offset(Point::new(*old_row_range.start(), 0))
.point_to_buffer_offset(Point::new(*old_row_range.start(), 0))
.unwrap();
let (_, buffer_end) = old_snapshot
.position_to_buffer_offset(Point::new(
.point_to_buffer_offset(Point::new(
*old_row_range.end(),
old_snapshot.line_len(MultiBufferRow(*old_row_range.end())),
))
@@ -2314,12 +2303,6 @@ impl Codegen {
} else {
let request = self.build_request(user_prompt, assistant_panel_context, cx)?;
// todo!
// println!(
// "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n{}",
// request
// );
let chunks =
cx.spawn(|_, cx| async move { model.stream_completion(request, &cx).await });
async move { Ok(chunks.await?.boxed()) }.boxed_local()
@@ -2334,8 +2317,8 @@ impl Codegen {
assistant_panel_context: Option<LanguageModelRequest>,
cx: &AppContext,
) -> Result<LanguageModelRequest> {
let multi_buffer = self.buffer.read(cx).snapshot(cx);
let language = multi_buffer.language_at(self.transform_range.start);
let buffer = self.buffer.read(cx).snapshot(cx);
let language = buffer.language_at(self.transform_range.start);
let language_name = if let Some(language) = language.as_ref() {
if Arc::ptr_eq(language, &language::PLAIN_TEXT) {
None
@@ -2360,55 +2343,54 @@ impl Codegen {
};
let language_name = language_name.as_deref();
let start = multi_buffer.position_to_buffer_offset(self.transform_range.start);
let end = multi_buffer.position_to_buffer_offset(self.transform_range.end);
let buffer = start
.zip(end)
.and_then(|((start_buffer, _), (end_buffer, _))| {
if start_buffer.remote_id() == end_buffer.remote_id() {
Some(start_buffer.clone())
} else {
None
}
})
.ok_or_else(|| anyhow::anyhow!("invalid transformation range"))?;
let start = buffer.point_to_buffer_offset(self.transform_range.start);
let end = buffer.point_to_buffer_offset(self.transform_range.end);
let (transform_buffer, transform_range) = if let Some((start, end)) = start.zip(end) {
let (start_buffer, start_buffer_offset) = start;
let (end_buffer, end_buffer_offset) = end;
if start_buffer.remote_id() == end_buffer.remote_id() {
(start_buffer.clone(), start_buffer_offset..end_buffer_offset)
} else {
return Err(anyhow::anyhow!("invalid transformation range"));
}
} else {
return Err(anyhow::anyhow!("invalid transformation range"));
};
let mut transform_context_range = transform_range.to_point(&transform_buffer);
transform_context_range.start.row = transform_context_range.start.row.saturating_sub(3);
transform_context_range.start.column = 0;
transform_context_range.end =
(transform_context_range.end + Point::new(3, 0)).min(transform_buffer.max_point());
transform_context_range.end.column =
transform_buffer.line_len(transform_context_range.end.row);
let transform_context_range = transform_context_range.to_offset(&transform_buffer);
let selected_ranges = self
.selected_ranges
.iter()
.filter_map(|selected_range| {
let start = multi_buffer
.position_to_buffer_point(selected_range.start)
let start = buffer
.point_to_buffer_offset(selected_range.start)
.map(|(_, offset)| offset)?;
let end = multi_buffer
.position_to_buffer_point(selected_range.end)
let end = buffer
.point_to_buffer_offset(selected_range.end)
.map(|(_, offset)| offset)?;
Some(start..end)
})
.collect::<Vec<_>>();
let inline_assist_prompt = if selected_ranges.len() == 1 {
let selected_range = &selected_ranges[0];
if selected_range.start == selected_range.end {
self.prompt_builder.build_insertion_prompt(
user_prompt,
language_name,
buffer,
selected_range.start,
)?
} else {
// Single non-empty range
// Implement logic for replacement
// TODO: Add replacement logic here
anyhow::bail!("Unsupported transformation!")
}
} else {
// Multiple ranges
// Implement logic for handling multiple ranges
// TODO: Add multiple range handling logic here
anyhow::bail!("Unsupported transformation!")
};
let prompt = self
.prompt_builder
.generate_content_prompt(
user_prompt,
language_name,
transform_buffer,
transform_range,
selected_ranges,
transform_context_range,
)
.map_err(|e| anyhow::anyhow!("Failed to generate content prompt: {}", e))?;
let mut messages = Vec::new();
if let Some(context_request) = assistant_panel_context {
@@ -2417,7 +2399,7 @@ impl Codegen {
messages.push(LanguageModelRequestMessage {
role: Role::User,
content: vec![inline_assist_prompt.into()],
content: vec![prompt.into()],
cache: false,
});
@@ -2441,15 +2423,25 @@ impl Codegen {
.collect::<Rope>();
let selection_start = edit_range.start.to_point(&snapshot);
let base_indent_size = snapshot.indent_size_for_line(MultiBufferRow(selection_start.row));
let indent_len_before_selection = base_indent_size.len.min(selection_start.column);
let base_indent = match base_indent_size.kind {
language::IndentKind::Space => " ".repeat(indent_len_before_selection as usize),
language::IndentKind::Tab => "\t".repeat(indent_len_before_selection as usize),
};
let mut generated_text = String::new();
let mut raw_output = String::new();
// Start with the indentation of the first line in the selection
let mut suggested_line_indent = snapshot
.suggested_indents(selection_start.row..=selection_start.row, cx)
.into_values()
.next()
.unwrap_or_else(|| snapshot.indent_size_for_line(MultiBufferRow(selection_start.row)));
// If the first line in the selection does not have indentation, check the following lines
if suggested_line_indent.len == 0 && suggested_line_indent.kind == IndentKind::Space {
for row in selection_start.row..=edit_range.end.to_point(&snapshot).row {
let line_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
// Prefer tabs if a line in the selection uses tabs as indentation
if line_indent.kind == IndentKind::Tab {
suggested_line_indent.kind = IndentKind::Tab;
break;
}
}
}
let telemetry = self.telemetry.clone();
self.diff = Diff::default();
@@ -2475,17 +2467,7 @@ impl Codegen {
response_latency = Some(request_start.elapsed());
}
let chunk = chunk?;
raw_output.push_str(&chunk);
let start_offset = generated_text.len();
for line in chunk.split_inclusive('\n') {
if generated_text.ends_with('\n') {
generated_text.push_str(&base_indent);
}
generated_text.push_str(line);
}
let char_ops = diff.push_new(&generated_text[start_offset..]);
let char_ops = diff.push_new(&chunk);
line_diff.push_char_operations(&char_ops, &selected_text);
diff_tx
.send((char_ops, line_diff.line_operations()))
@@ -2503,10 +2485,6 @@ impl Codegen {
};
let result = diff.await;
// todo!
// println!("Base indent: {:?}", base_indent_size);
// println!("Raw output: {:?}", raw_output);
// println!("Generated text: {:?}", generated_text);
let error_message =
result.as_ref().err().map(|error| error.to_string());

View File

@@ -4,23 +4,10 @@ use futures::StreamExt;
use handlebars::{Handlebars, RenderError, TemplateError};
use language::BufferSnapshot;
use parking_lot::Mutex;
use rope::Point;
use serde::Serialize;
use std::{cmp, ops::Range, sync::Arc, time::Duration};
use std::{ops::Range, sync::Arc, time::Duration};
use util::ResultExt;
#[derive(Serialize)]
pub struct InsertionContext {
pub document_prefix: String,
pub document_suffix: String,
pub context_prefix: String,
pub context_suffix: String,
pub prompt: String,
pub language: String,
pub content_type: String,
pub truncated: bool,
}
#[derive(Serialize)]
pub struct ContentPromptContext {
pub content_type: String,
@@ -173,7 +160,6 @@ impl PromptBuilder {
.map_err(Box::new)
};
register_template("insertion")?;
register_template("content_prompt")?;
register_template("terminal_assistant_prompt")?;
register_template("edit_workflow")?;
@@ -182,59 +168,6 @@ impl PromptBuilder {
Ok(())
}
pub fn build_insertion_prompt(
&self,
user_prompt: String,
language_name: Option<&str>,
buffer: BufferSnapshot,
position: Point,
) -> Result<String, RenderError> {
let mut document_prefix = String::new();
for chunk in buffer.text_for_range(Point::zero()..position) {
document_prefix.push_str(chunk);
}
let mut document_suffix = String::new();
for chunk in buffer.text_for_range(position..buffer.max_point()) {
document_suffix.push_str(chunk);
}
// Get 5 lines of context above and below the insertion
let mut context_range = position..position;
context_range.start.row = context_range.start.row.saturating_sub(5);
context_range.start.column = 0;
context_range.end = cmp::min(context_range.end + Point::new(5, 0), buffer.max_point());
context_range.end.column = buffer.line_len(context_range.end.row);
let mut context_prefix = String::new();
let mut context_suffix = String::new();
for chunk in buffer.text_for_range(context_range.start..position) {
context_prefix.push_str(chunk);
}
for chunk in buffer.text_for_range(position..context_range.end) {
context_suffix.push_str(chunk);
}
let language = language_name.unwrap_or("Unknown").to_string();
let content_type = match language_name {
None | Some("Markdown" | "Plain Text") => "text".to_string(),
Some(_) => "code".to_string(),
};
let truncated = false; // Assuming no truncation for now
let context = InsertionContext {
document_prefix,
document_suffix,
context_prefix,
context_suffix,
prompt: user_prompt,
language,
content_type,
truncated,
};
self.handlebars.lock().render("insertion", &context)
}
pub fn generate_content_prompt(
&self,
user_prompt: String,

View File

@@ -152,25 +152,6 @@ pub struct IndentSize {
pub kind: IndentKind,
}
impl std::fmt::Display for IndentSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.kind {
IndentKind::Space => {
for _ in 0..self.len {
write!(f, " ")?;
}
Ok(())
}
IndentKind::Tab => {
for _ in 0..self.len {
write!(f, "\t")?;
}
Ok(())
}
}
}
}
/// A whitespace character that's used for indentation.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum IndentKind {

View File

@@ -88,7 +88,12 @@ impl<T> Outline<T> {
}
/// Find the most similar symbol to the provided query using normalized Levenshtein distance.
pub fn find_most_similar(&self, query: &str) -> Option<(SymbolPath, &OutlineItem<T>)> {
pub fn find_most_similar(&self, symbol: &str) -> Option<(SymbolPath, &OutlineItem<T>)> {
// Sometimes the model incorrectly includes a space or colon in the query,
// e.g. in Elm, Roc, etc. it might say `foo : ...` because it's trying to include the symbol's type,
// which isn't part of the symbol and won't be in the outline. Trim the first space and anything after it.
let symbol = &symbol[..symbol.find(' ').unwrap_or(symbol.len())];
const SIMILARITY_THRESHOLD: f64 = 0.6;
let (position, similarity) = self
@@ -96,7 +101,7 @@ impl<T> Outline<T> {
.iter()
.enumerate()
.map(|(index, candidate)| {
let similarity = strsim::normalized_levenshtein(&candidate.string, query);
let similarity = strsim::normalized_levenshtein(&candidate.string, symbol);
(index, similarity)
})
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())?;

View File

@@ -29,22 +29,15 @@ const PROVIDER_NAME: &str = "Anthropic";
pub struct AnthropicSettings {
pub api_url: String,
pub low_speed_timeout: Option<Duration>,
/// Extend Zed's list of Anthropic models.
pub available_models: Vec<AvailableModel>,
pub needs_setting_migration: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
/// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-20240620
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: usize,
/// 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<u32>,
}
@@ -178,7 +171,6 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
model.name.clone(),
anthropic::Model::Custom {
name: model.name.clone(),
display_name: model.display_name.clone(),
max_tokens: model.max_tokens,
tool_override: model.tool_override.clone(),
cache_configuration: model.cache_configuration.as_ref().map(|config| {

View File

@@ -52,20 +52,12 @@ pub enum AvailableProvider {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AvailableModel {
/// The provider of the language model.
pub provider: AvailableProvider,
/// The model's name in the provider's API. e.g. claude-3-5-sonnet-20240620
pub name: String,
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
pub display_name: Option<String>,
/// The size of the context window, indicating the maximum number of tokens the model can process.
pub max_tokens: usize,
/// The maximum number of output tokens allowed by the model.
pub max_output_tokens: Option<u32>,
/// Override this model with a different Anthropic model for tool calls.
pub tool_override: Option<String>,
/// Indicates whether this custom model supports caching.
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
provider: AvailableProvider,
name: String,
max_tokens: usize,
tool_override: Option<String>,
cache_configuration: Option<LanguageModelCacheConfiguration>,
max_output_tokens: Option<u32>,
}
pub struct CloudLanguageModelProvider {
@@ -210,7 +202,6 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
AvailableProvider::Anthropic => {
CloudModel::Anthropic(anthropic::Model::Custom {
name: model.name.clone(),
display_name: model.display_name.clone(),
max_tokens: model.max_tokens,
tool_override: model.tool_override.clone(),
cache_configuration: model.cache_configuration.as_ref().map(|config| {

View File

@@ -196,24 +196,6 @@ pub struct LanguageModelRequestMessage {
pub cache: bool,
}
impl std::fmt::Display for LanguageModelRequestMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Role: {}\n", self.role)?;
write!(f, "Content:\n")?;
for (index, content) in self.content.iter().enumerate() {
match content {
MessageContent::Text(text) => write!(f, " {}: Text ({})\n", index, text)?,
MessageContent::Image(image) => write!(
f,
" {}: Image ({}x{})\n",
index, image.size.width.0, image.size.height.0
)?,
}
}
write!(f, "Cache: {}", self.cache)
}
}
impl LanguageModelRequestMessage {
pub fn string_contents(&self) -> String {
let mut string_buffer = String::new();
@@ -246,20 +228,6 @@ pub struct LanguageModelRequest {
pub temperature: f32,
}
impl std::fmt::Display for LanguageModelRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "LanguageModelRequest {{")?;
writeln!(f, " messages: [")?;
for message in &self.messages {
writeln!(f, " {}", message)?;
}
writeln!(f, " ],")?;
writeln!(f, " stop: {:?},", self.stop)?;
writeln!(f, " temperature: {}", self.temperature)?;
write!(f, "}}")
}
}
impl LanguageModelRequest {
pub fn into_open_ai(self, model: String) -> open_ai::Request {
open_ai::Request {

View File

@@ -94,14 +94,12 @@ impl AnthropicSettingsContent {
.filter_map(|model| match model {
anthropic::Model::Custom {
name,
display_name,
max_tokens,
tool_override,
cache_configuration,
max_output_tokens,
} => Some(provider::anthropic::AvailableModel {
name,
display_name,
max_tokens,
tool_override,
cache_configuration: cache_configuration.as_ref().map(

View File

@@ -2650,11 +2650,11 @@ impl MultiBufferSnapshot {
}
}
pub fn position_to_buffer_offset<T: ToOffset>(
pub fn point_to_buffer_offset<T: ToOffset>(
&self,
position: T,
point: T,
) -> Option<(&BufferSnapshot, usize)> {
let offset = position.to_offset(self);
let offset = point.to_offset(self);
let mut cursor = self.excerpts.cursor::<usize>();
cursor.seek(&offset, Bias::Right, &());
if cursor.item().is_none() {
@@ -2668,24 +2668,6 @@ impl MultiBufferSnapshot {
})
}
pub fn position_to_buffer_point<T: ToPoint>(
&self,
position: T,
) -> Option<(&BufferSnapshot, Point)> {
let point = position.to_point(self);
let mut cursor = self.excerpts.cursor::<Point>();
cursor.seek(&point, Bias::Right, &());
if cursor.item().is_none() {
cursor.prev(&());
}
cursor.item().map(|excerpt| {
let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
let buffer_point = excerpt_start + point - *cursor.start();
(&excerpt.buffer, buffer_point)
})
}
pub fn suggested_indents(
&self,
rows: impl IntoIterator<Item = u32>,
@@ -3466,12 +3448,12 @@ impl MultiBufferSnapshot {
}
pub fn file_at<T: ToOffset>(&self, point: T) -> Option<&Arc<dyn File>> {
self.position_to_buffer_offset(point)
self.point_to_buffer_offset(point)
.and_then(|(buffer, _)| buffer.file())
}
pub fn language_at<T: ToOffset>(&self, point: T) -> Option<&Arc<Language>> {
self.position_to_buffer_offset(point)
self.point_to_buffer_offset(point)
.and_then(|(buffer, offset)| buffer.language_at(offset))
}
@@ -3482,7 +3464,7 @@ impl MultiBufferSnapshot {
) -> &'a LanguageSettings {
let mut language = None;
let mut file = None;
if let Some((buffer, offset)) = self.position_to_buffer_offset(point) {
if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
language = buffer.language_at(offset);
file = buffer.file();
}
@@ -3490,7 +3472,7 @@ impl MultiBufferSnapshot {
}
pub fn language_scope_at<T: ToOffset>(&self, point: T) -> Option<LanguageScope> {
self.position_to_buffer_offset(point)
self.point_to_buffer_offset(point)
.and_then(|(buffer, offset)| buffer.language_scope_at(offset))
}
@@ -3499,7 +3481,7 @@ impl MultiBufferSnapshot {
position: T,
cx: &AppContext,
) -> Option<IndentSize> {
let (buffer_snapshot, offset) = self.position_to_buffer_offset(position)?;
let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
Some(buffer_snapshot.language_indent_size_at(offset, cx))
}

View File

@@ -1275,15 +1275,6 @@ mod tests {
max: u8,
}
/// Represents a summary of integers with various properties.
///
/// This struct provides a summary of a collection of integers, including:
/// - The count of integers
/// - The sum of all integers
/// - Whether the collection contains any even numbers
/// - The maximum value in the collection
///
/// It is used as the summary type for the `SumTree` when storing `u8` values.
#[derive(Ord, PartialOrd, Default, Eq, PartialEq, Clone, Debug)]
struct Count(usize);

View File

@@ -166,7 +166,7 @@ fn active_item_selection_properties(
let multi_buffer = editor.buffer().clone();
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
let (buffer_snapshot, buffer_offset) =
multi_buffer_snapshot.position_to_buffer_offset(selection.head())?;
multi_buffer_snapshot.point_to_buffer_offset(selection.head())?;
let buffer_anchor = buffer_snapshot.anchor_before(buffer_offset);
let buffer = multi_buffer.read(cx).buffer(buffer_snapshot.remote_id())?;
Some(Location {

View File

@@ -943,31 +943,26 @@ impl Item for TerminalView {
fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
let terminal = self.terminal().read(cx);
let title = terminal.title(true);
let rerun_button = |task_id: task::TaskId| {
IconButton::new("rerun-icon", IconName::Rerun)
.icon_size(IconSize::Small)
.size(ButtonSize::Compact)
.icon_color(Color::Default)
.shape(ui::IconButtonShape::Square)
.tooltip(|cx| Tooltip::text("Rerun task", cx))
.on_click(move |_, cx| {
cx.dispatch_action(Box::new(tasks_ui::Rerun {
task_id: Some(task_id.clone()),
..tasks_ui::Rerun::default()
}));
})
};
let (icon, icon_color, rerun_button) = match terminal.task() {
Some(terminal_task) => match &terminal_task.status {
TaskStatus::Unknown => (IconName::ExclamationTriangle, Color::Warning, None),
TaskStatus::Running => (IconName::Play, Color::Disabled, None),
TaskStatus::Unknown => (
IconName::ExclamationTriangle,
Color::Warning,
Some(rerun_button(terminal_task.id.clone())),
),
TaskStatus::Completed { success } => {
let rerun_button = rerun_button(terminal_task.id.clone());
let task_id = terminal_task.id.clone();
let rerun_button = IconButton::new("rerun-icon", IconName::Rerun)
.icon_size(IconSize::Small)
.size(ButtonSize::Compact)
.icon_color(Color::Default)
.shape(ui::IconButtonShape::Square)
.tooltip(|cx| Tooltip::text("Rerun task", cx))
.on_click(move |_, cx| {
cx.dispatch_action(Box::new(tasks_ui::Rerun {
task_id: Some(task_id.clone()),
..Default::default()
}));
});
if *success {
(IconName::Check, Color::Success, Some(rerun_button))
} else {