Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/mitocode/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import argparse
import asyncio
from pyexpat.errors import messages

from httpx import stream

from mitocode.backend.agent.agent_orchestrator import AgentOrchestrator
from mitocode.backend.chat_provider.scenario_provider import ScenarioProvider
from mitocode.backend.chat_provider.chat_provider_openai import ChatProviderOpenAI
from mitocode.backend.tool.tool_manager import ToolManager
from mitocode.tui.ui_main import VibeFrontend

Expand Down Expand Up @@ -65,7 +69,28 @@ def main():
tool_manager = ToolManager()
_register_tools(tool_manager)

provider = ScenarioProvider(delay=args.speed)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Speed option is ignored 🐞 Bug ≡ Correctness

The CLI still advertises --speed as the delay between chunks, but the replacement provider
construction no longer uses args.speed. Every supplied value is silently ignored, so the option
cannot affect streaming as documented.
Agent Prompt
## Issue description
The documented --speed argument is parsed but no longer affects the selected provider.

## Issue Context
The removed ScenarioProvider construction passed args.speed; ChatProviderOpenAI has no equivalent delay behavior.

## Fix Focus Areas
- src/mitocode/__main__.py[61-83]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

provider = asyncio.run(ChatProviderOpenAI.factory(
default_model= "nvidia/nemotron-3.5-lightning:free",
default_model_list= None,
Comment on lines +72 to +74

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Cli requires live model discovery 🐞 Bug ☼ Reliability

The CLI now constructs ChatProviderOpenAI with no fallback model list, whose factory performs a live
model-list request and raises when that request fails. A network outage, unreachable OpenRouter
endpoint, or authentication failure therefore prevents the TUI from launching instead of retaining
the previous offline scenario behavior.
Agent Prompt
## Issue description
CLI startup now requires successful live OpenRouter model discovery, so provider initialization failure prevents the TUI from launching.

## Issue Context
The previous ScenarioProvider was offline. ChatProviderOpenAI.factory calls init, and init fetches models; without a fallback model list, failures are raised.

## Fix Focus Areas
- src/mitocode/__main__.py[72-83]
- src/mitocode/backend/chat_provider/chat_provider_openai.py[56-96]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

default_messages= None,
default_tools= None,
stream= True,
settings={
"api_key": "sk-别看了这次我不会再把key提交到仓库了!",
"base_url": "https://openrouter.ai/api/v1",
"timeout": 60,
})
)

# print(asyncio.run(provider.chat(
# model ="nvidia/nemotron-3.5-lightning:free",
# tools =None,
# messages = [
# {"role": "system", "content": "You are a helpful assistant."},
# {"role": "user", "content": "Hello!"}
# ],
# stream = False
# )))

orchestrator = AgentOrchestrator(
tool_manager=tool_manager, default_provider=provider
Expand Down
98 changes: 75 additions & 23 deletions src/mitocode/backend/chat_provider/scenario_provider.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"""旧实现,与现有ABC不符,已弃用,请勿参考!"""

"""Scripted mock provider replaying a fixed multi-turn tool-call demo.

Used for offline demos and TUI development: each ``chat`` call plays back
Expand All @@ -9,7 +7,10 @@
import asyncio
import json
from types import SimpleNamespace
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, AsyncGenerator

from mitocode.backend.chat_provider.sample_chat_provider import _SampleChatProvider



def _make_chunk(
Expand Down Expand Up @@ -139,52 +140,84 @@ def _chunk_tool_call(
return chunks


class ScenarioProvider:
class ScenarioProvider(_SampleChatProvider):
"""Mock provider that plays back a multi-turn tool-call scenario.

Each call to :meth:`chat` advances an internal turn counter and returns
the next pre-recorded stream: markdown text, bash, write_file, read_file
the next pre-recorded stream: markdown text, bash / write_file / read_file
tool calls with bubble sort code.
"""

def __init__(self, delay: float = 0.03):
def __init__(
self,
default_model: str = "scenario-model",
default_model_list: Optional[List[str]] = None,
default_messages: Optional[List[Dict]] = None,
default_tools: Optional[List[Dict]] = None,
stream: bool = True,
settings: Optional[Dict[str, Any]] = None,
delay: float = 0.03,
):
"""Initialize the provider with an inter-chunk streaming delay.

Args:
delay: Seconds to sleep between yielded chunks (default 0.03).
"""
if default_model_list is None:
default_model_list = ["scenario-model"]
super().__init__(
default_model=default_model,
default_model_list=default_model_list,
default_messages=default_messages,
default_tools=default_tools,
stream=stream,
settings=settings,
)
self.turn = 0
self._delay = delay

@staticmethod
def get_model_list():
"""Return the model identifiers accepted by this provider."""
return ["scenario-model"]
@classmethod
async def factory(cls, *args, **kwargs) -> "ScenarioProvider":
"""Create and initialize a provider instance asynchronously."""
ins = cls(*args, **kwargs)
await ins.init()
return ins

@classmethod
def get_settings_schema(cls):
def get_settings_schema(cls) -> Dict[str, Any]:
"""Return the JSON Schema describing accepted settings."""
return {
"type": "object",
"properties": {},
"properties": {
"delay": {"type": "number", "minimum": 0, "default": 0.03},
},
"additionalProperties": True,
}

@staticmethod
def check_settings_schema(settings):
"""Validate that *settings* is a dict; any keys are allowed."""
return isinstance(settings, dict)
@classmethod
def check_settings_schema(cls, settings: Any) -> bool:
"""Validate that *settings* is a dict with optional delay."""
if not isinstance(settings, dict):
return False
delay = settings.get("delay", 0.03)
return isinstance(delay, (int, float)) and delay >= 0

async def get_model_list(self) -> List[str]:
"""Return the model identifiers accepted by this provider."""
return ["scenario-model"]

def init(self):
async def init(self) -> None:
"""No-op initialization hook kept for provider interface parity."""
pass
delay = self.settings.get("delay", 0.03) if self.settings else 0.03
self._delay = delay
Comment on lines +209 to +212

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Factory discards delay argument 🐞 Bug ≡ Correctness

ScenarioProvider.factory(delay=x) stores x in the constructor and then init immediately overwrites
it with settings.delay or 0.03. Callers using the provider's standard factory lifecycle therefore
cannot configure the stream delay through the explicit delay parameter.
Agent Prompt
## Issue description
Factory initialization overwrites the explicit delay constructor argument with the default settings value.

## Issue Context
The constructor assigns self._delay from delay, after which factory invokes init and resets it.

## Fix Focus Areas
- src/mitocode/backend/chat_provider/scenario_provider.py[151-177]
- src/mitocode/backend/chat_provider/scenario_provider.py[179-184]
- src/mitocode/backend/chat_provider/scenario_provider.py[209-212]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


async def chat(
self,
tools: Optional[list] = None,
messages: Optional[List[Dict[str, Any]]] = None,
**kwargs,
):
model: str = "",
tools: Optional[List[Dict]] = None,
messages: Optional[List[Dict]] = None,
stream: bool = True,
) -> AsyncGenerator | Dict:
"""Return an async stream playing back the next scripted turn.

Each invocation advances the internal turn counter: turns 0-2 emit
Expand Down Expand Up @@ -231,4 +264,23 @@ async def _stream():
yield c
await asyncio.sleep(self._delay)

return _stream()
if stream:
return _stream()

full_content = ""
for c in chunks:
if c.choices[0].delta.content:
full_content += c.choices[0].delta.content
Comment on lines +270 to +273

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Non-streaming drops tool calls 🐞 Bug ≡ Correctness

For scripted turns 0–2, the new non-streaming branch copies only delta.content, omits
message.tool_calls, and hardcodes finish_reason to stop. Consumers requesting stream=False receive a
false normal completion and cannot execute the scenario's bash, write_file, or read_file calls.
Agent Prompt
## Issue description
Non-streaming scenario completions discard fragmented tool calls and return the wrong finish reason.

## Issue Context
Turns 0–2 generate tool-call deltas ending in tool_calls. Reconstruct their IDs, names, and arguments into message.tool_calls and preserve the actual finish reason.

## Fix Focus Areas
- src/mitocode/backend/chat_provider/scenario_provider.py[233-260]
- src/mitocode/backend/chat_provider/scenario_provider.py[267-286]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

return {
"choices": [
{
"message": {
"role": "assistant",
"content": full_content,
},
"finish_reason": "stop",
"index": 0,
}
],
"model": model or self.default_model,
}
Loading