mirror of
https://github.com/Aider-AI/aider.git
synced 2025-06-02 18:54:59 +00:00
feat: use copy-paste instead of api
This commit is contained in:
parent
56b45ce1d3
commit
8bc7e32fa7
4 changed files with 82 additions and 4 deletions
|
@ -647,6 +647,12 @@ def get_parser(default_config_files, git_root):
|
||||||
default=False,
|
default=False,
|
||||||
help="Enable automatic copy/paste of chat between aider and web UI (default: False)",
|
help="Enable automatic copy/paste of chat between aider and web UI (default: False)",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--copy-paste-no-api",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
help="Use automatic copy/paste of chat between aider and web UI instead of API (default: False)",
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--apply",
|
"--apply",
|
||||||
metavar="FILE",
|
metavar="FILE",
|
||||||
|
|
|
@ -211,6 +211,9 @@ class Coder:
|
||||||
main_model = self.main_model
|
main_model = self.main_model
|
||||||
weak_model = main_model.weak_model
|
weak_model = main_model.weak_model
|
||||||
|
|
||||||
|
if main_model.copy_paste_no_api:
|
||||||
|
lines.append("Running in copy-paste mode instead of using API")
|
||||||
|
|
||||||
if weak_model is not main_model:
|
if weak_model is not main_model:
|
||||||
prefix = "Main model"
|
prefix = "Main model"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -820,6 +820,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||||
editor_model=args.editor_model,
|
editor_model=args.editor_model,
|
||||||
editor_edit_format=args.editor_edit_format,
|
editor_edit_format=args.editor_edit_format,
|
||||||
verbose=args.verbose,
|
verbose=args.verbose,
|
||||||
|
copy_paste_no_api=args.copy_paste_no_api,
|
||||||
|
io=io,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if deprecated remove_reasoning is set
|
# Check if deprecated remove_reasoning is set
|
||||||
|
@ -948,6 +950,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||||
if args.cache_prompts and args.map_refresh == "auto":
|
if args.cache_prompts and args.map_refresh == "auto":
|
||||||
args.map_refresh = "files"
|
args.map_refresh = "files"
|
||||||
|
|
||||||
|
if args.copy_paste_no_api:
|
||||||
|
args.stream = False
|
||||||
|
|
||||||
if not main_model.streaming:
|
if not main_model.streaming:
|
||||||
if args.stream:
|
if args.stream:
|
||||||
io.tool_warning(
|
io.tool_warning(
|
||||||
|
|
|
@ -7,11 +7,13 @@ import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
from dataclasses import dataclass, fields
|
from dataclasses import dataclass, fields
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
import json5
|
import json5
|
||||||
|
import pyperclip
|
||||||
import yaml
|
import yaml
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
@ -304,9 +306,7 @@ model_info_manager = ModelInfoManager()
|
||||||
|
|
||||||
|
|
||||||
class Model(ModelSettings):
|
class Model(ModelSettings):
|
||||||
def __init__(
|
def __init__(self, model, weak_model=None, editor_model=None, editor_edit_format=None, verbose=False, copy_paste_no_api=False, io=None):
|
||||||
self, model, weak_model=None, editor_model=None, editor_edit_format=None, verbose=False
|
|
||||||
):
|
|
||||||
# Map any alias to its canonical name
|
# Map any alias to its canonical name
|
||||||
model = MODEL_ALIASES.get(model, model)
|
model = MODEL_ALIASES.get(model, model)
|
||||||
|
|
||||||
|
@ -317,6 +317,9 @@ class Model(ModelSettings):
|
||||||
self.weak_model = None
|
self.weak_model = None
|
||||||
self.editor_model = None
|
self.editor_model = None
|
||||||
|
|
||||||
|
self.io = io
|
||||||
|
self.copy_paste_no_api=copy_paste_no_api
|
||||||
|
|
||||||
# Find the extra settings
|
# Find the extra settings
|
||||||
self.extra_model_settings = next(
|
self.extra_model_settings = next(
|
||||||
(ms for ms in MODEL_SETTINGS if ms.name == "aider/extra_params"), None
|
(ms for ms in MODEL_SETTINGS if ms.name == "aider/extra_params"), None
|
||||||
|
@ -334,7 +337,7 @@ class Model(ModelSettings):
|
||||||
# with minimum 1k and maximum 8k
|
# with minimum 1k and maximum 8k
|
||||||
self.max_chat_history_tokens = min(max(max_input_tokens / 16, 1024), 8192)
|
self.max_chat_history_tokens = min(max(max_input_tokens / 16, 1024), 8192)
|
||||||
|
|
||||||
self.configure_model_settings(model)
|
self.configure_model_settings(model)
|
||||||
if weak_model is False:
|
if weak_model is False:
|
||||||
self.weak_model_name = None
|
self.weak_model_name = None
|
||||||
else:
|
else:
|
||||||
|
@ -345,6 +348,10 @@ class Model(ModelSettings):
|
||||||
else:
|
else:
|
||||||
self.get_editor_model(editor_model, editor_edit_format)
|
self.get_editor_model(editor_model, editor_edit_format)
|
||||||
|
|
||||||
|
if self.copy_paste_no_api:
|
||||||
|
self.weak_model = self
|
||||||
|
self.editor_model = self
|
||||||
|
|
||||||
def get_model_info(self, model):
|
def get_model_info(self, model):
|
||||||
return model_info_manager.get_model_info(model)
|
return model_info_manager.get_model_info(model)
|
||||||
|
|
||||||
|
@ -874,6 +881,9 @@ class Model(ModelSettings):
|
||||||
return self.name.startswith("ollama/") or self.name.startswith("ollama_chat/")
|
return self.name.startswith("ollama/") or self.name.startswith("ollama_chat/")
|
||||||
|
|
||||||
def send_completion(self, messages, functions, stream, temperature=None):
|
def send_completion(self, messages, functions, stream, temperature=None):
|
||||||
|
if self.copy_paste_no_api:
|
||||||
|
return self.copy_paste_completion(messages)
|
||||||
|
|
||||||
if os.environ.get("AIDER_SANITY_CHECK_TURNS"):
|
if os.environ.get("AIDER_SANITY_CHECK_TURNS"):
|
||||||
sanity_check_messages(messages)
|
sanity_check_messages(messages)
|
||||||
|
|
||||||
|
@ -917,6 +927,57 @@ class Model(ModelSettings):
|
||||||
res = litellm.completion(**kwargs)
|
res = litellm.completion(**kwargs)
|
||||||
return hash_object, res
|
return hash_object, res
|
||||||
|
|
||||||
|
def copy_paste_completion(self, messages):
|
||||||
|
formatted_messages = "\n".join(
|
||||||
|
f"{msg['content']}" for msg in messages if msg.get("content")
|
||||||
|
)
|
||||||
|
|
||||||
|
pyperclip.copy(formatted_messages)
|
||||||
|
|
||||||
|
if self.io is not None:
|
||||||
|
self.io.tool_output(
|
||||||
|
"""✓ Request copied to clipboard
|
||||||
|
→ Paste into LLM web UI
|
||||||
|
← Copy response back to clipboard
|
||||||
|
Monitoring clipboard for changes..."""
|
||||||
|
)
|
||||||
|
|
||||||
|
last_clipboard = pyperclip.paste()
|
||||||
|
while last_clipboard == pyperclip.paste():
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
response = pyperclip.paste()
|
||||||
|
|
||||||
|
completion = litellm.ModelResponse(
|
||||||
|
id=f"chatcmpl-{uuid.uuid4()}",
|
||||||
|
choices=[
|
||||||
|
{
|
||||||
|
"message": {
|
||||||
|
"role": "assistant",
|
||||||
|
"content": response,
|
||||||
|
"function_call": None,
|
||||||
|
},
|
||||||
|
"finish_reason": "stop",
|
||||||
|
"index": 0,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
created=int(time.time()),
|
||||||
|
model=self.name,
|
||||||
|
usage={"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
|
||||||
|
object="chat.completion",
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs = dict(
|
||||||
|
model=self.name,
|
||||||
|
messages=messages,
|
||||||
|
stream=False
|
||||||
|
)
|
||||||
|
|
||||||
|
key = json.dumps(kwargs, sort_keys=True).encode()
|
||||||
|
hash_object = hashlib.sha1(key)
|
||||||
|
|
||||||
|
return hash_object, completion
|
||||||
|
|
||||||
def simple_send_with_retries(self, messages):
|
def simple_send_with_retries(self, messages):
|
||||||
from aider.exceptions import LiteLLMExceptions
|
from aider.exceptions import LiteLLMExceptions
|
||||||
|
|
||||||
|
@ -1047,6 +1108,9 @@ def sanity_check_models(io, main_model):
|
||||||
def sanity_check_model(io, model):
|
def sanity_check_model(io, model):
|
||||||
show = False
|
show = False
|
||||||
|
|
||||||
|
if model.copy_paste_no_api:
|
||||||
|
return show
|
||||||
|
|
||||||
if model.missing_keys:
|
if model.missing_keys:
|
||||||
show = True
|
show = True
|
||||||
io.tool_warning(f"Warning: {model} expects these environment variables")
|
io.tool_warning(f"Warning: {model} expects these environment variables")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue