Compare commits
4 Commits
56352a19b0
...
fb78d813af
| Author | SHA1 | Date | |
|---|---|---|---|
| fb78d813af | |||
| 65088c2fdc | |||
| e4e47c4649 | |||
| 783c5c2b87 |
@@ -2,4 +2,4 @@ TG_API_ID=123456
|
|||||||
TG_API_HASH=your_api_hash_here
|
TG_API_HASH=your_api_hash_here
|
||||||
SESSION_DIR=sessions
|
SESSION_DIR=sessions
|
||||||
ACCOUNTS_JSON=[{"name":"personal","session":"personal"},{"name":"work","session":"work"}]
|
ACCOUNTS_JSON=[{"name":"personal","session":"personal"},{"name":"work","session":"work"}]
|
||||||
# AI_CONFIG={"default_model":"mm","context_messages":12,"prompt":{"root":"prompts/ai","system_file":"system.md","parts_dir":"parts"},"image_understand":{"api_key":"your_minimax_token_plan_key_here","api_host":"https://api.minimax.chat","command":["uvx","minimax-coding-plan-mcp","-y"]},"providers":{"minimax":{"api_key":"your_minimax_api_key_here","base_url":"https://api.minimax.chat/v1"},"openrouter":{"api_key":"your_openrouter_api_key_here","base_url":"https://openrouter.ai/api/v1"}},"models":{"mm":{"provider":"minimax","model":"MiniMax-Text-01"},"or":{"provider":"openrouter","model":"openai/gpt-4o-mini"}}}
|
# AI_CONFIG={"default_model":"mm","context_messages":12,"prompt":{"root":"prompts/ai","system_file":"system.md","parts_dir":"parts"},"image_understand":{"api_key":"your_minimax_token_plan_key_here","api_host":"https://api.minimax.chat","command":["uvx","minimax-coding-plan-mcp","-y"]},"providers":{"minimax":{"api_key":"your_minimax_api_key_here","base_url":"https://api.minimax.io/v1"},"openrouter":{"api_key":"your_openrouter_api_key_here","base_url":"https://openrouter.ai/api/v1"}},"models":{"mm":{"provider":"minimax","model":"MiniMax-Text-01"},"or":{"provider":"openrouter","model":"openai/gpt-4o-mini"}}}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from pathlib import Path
|
|||||||
import tempfile
|
import tempfile
|
||||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable
|
from typing import TYPE_CHECKING, Any, Awaitable, Callable
|
||||||
|
|
||||||
|
import httpx
|
||||||
from openai import AsyncOpenAI
|
from openai import AsyncOpenAI
|
||||||
|
|
||||||
from app.ai.image_understand import (
|
from app.ai.image_understand import (
|
||||||
@@ -28,6 +29,15 @@ log = logging.getLogger(__name__)
|
|||||||
ProgressCallback = Callable[[str], Awaitable[None] | None]
|
ProgressCallback = Callable[[str], Awaitable[None] | None]
|
||||||
|
|
||||||
|
|
||||||
|
async def _sanitize_response_encoding(response: httpx.Response) -> None:
|
||||||
|
await response.aread()
|
||||||
|
raw = response.content
|
||||||
|
try:
|
||||||
|
raw.decode("utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
response._content = raw.decode("utf-8", errors="replace").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class AIResponse:
|
class AIResponse:
|
||||||
text: str
|
text: str
|
||||||
@@ -103,6 +113,9 @@ class AIService:
|
|||||||
api_key=provider.api_key,
|
api_key=provider.api_key,
|
||||||
base_url=provider.base_url,
|
base_url=provider.base_url,
|
||||||
timeout=provider.timeout,
|
timeout=provider.timeout,
|
||||||
|
http_client=httpx.AsyncClient(
|
||||||
|
event_hooks={"response": [_sanitize_response_encoding]},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self._clients[provider.name] = client
|
self._clients[provider.name] = client
|
||||||
return client
|
return client
|
||||||
|
|||||||
@@ -309,26 +309,14 @@ async def ai(ctx: "CommandContext") -> None:
|
|||||||
await ctx.reply(str(exc))
|
await ctx.reply(str(exc))
|
||||||
return
|
return
|
||||||
|
|
||||||
status = await ctx.reply(
|
async def _react(emoji: str | None) -> None:
|
||||||
"\n".join(
|
|
||||||
[
|
|
||||||
"⏳ AI request started",
|
|
||||||
f"model: {selected_alias}",
|
|
||||||
f"context: {invocation.context_messages} message(s)",
|
|
||||||
"tools: understand_image"
|
|
||||||
if invocation.use_image_understanding
|
|
||||||
else "tools: none",
|
|
||||||
f"prompt: {invocation.prompt[:200]}{'...' if len(invocation.prompt) > 200 else ''}",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async def _progress(text: str) -> None:
|
|
||||||
try:
|
try:
|
||||||
await status.edit_text(text)
|
await ctx.client.send_reaction(ctx.message.chat.id, ctx.message.id, emoji=emoji)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
await _react("🤔")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = await ai_service.generate(
|
response = await ai_service.generate(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -338,16 +326,11 @@ async def ai(ctx: "CommandContext") -> None:
|
|||||||
use_image_understanding=invocation.use_image_understanding,
|
use_image_understanding=invocation.use_image_understanding,
|
||||||
prompt=invocation.prompt,
|
prompt=invocation.prompt,
|
||||||
),
|
),
|
||||||
progress=_progress,
|
|
||||||
)
|
)
|
||||||
except (RuntimeError, ValueError) as exc:
|
except (RuntimeError, ValueError) as exc:
|
||||||
await _progress(f"❌ {exc}")
|
await _react("👎")
|
||||||
|
await ctx.reply(str(exc))
|
||||||
return
|
return
|
||||||
|
|
||||||
final_text = f"✅\n{response.text}"
|
await _react("👍")
|
||||||
if len(final_text) <= 3900:
|
|
||||||
await _progress(final_text)
|
|
||||||
return
|
|
||||||
|
|
||||||
await _progress("✅ Response ready")
|
|
||||||
await _reply_long(ctx, response.text)
|
await _reply_long(ctx, response.text)
|
||||||
|
|||||||
Reference in New Issue
Block a user