Fixed auto approver and inference continuation
This commit is contained in:
@@ -178,7 +178,5 @@ class AutoApprover:
|
|||||||
def _response_approval_thread(self) -> None:
|
def _response_approval_thread(self) -> None:
|
||||||
if self._stop_event.wait(self._response_timeout):
|
if self._stop_event.wait(self._response_timeout):
|
||||||
return
|
return
|
||||||
if (self._response_enabled and
|
if self._response_enabled:
|
||||||
self.agent.llms[self._llm_name] == LlmState.IDLE):
|
|
||||||
response = self.agent.response_buffer.get_text()
|
|
||||||
self.agent.approve_response()
|
self.agent.approve_response()
|
||||||
|
|||||||
@@ -1,84 +1,87 @@
|
|||||||
from huggingface_hub import InferenceClient
|
from huggingface_hub import InferenceClient
|
||||||
from transformers import AutoTokenizer, AutoConfig
|
from transformers import AutoTokenizer, AutoConfig
|
||||||
from typing import Iterator, Optional, Callable
|
from typing import Iterator, Optional, Callable
|
||||||
|
|
||||||
from . import LlmEngine
|
from . import LlmEngine
|
||||||
|
|
||||||
class HfLlmEngine(LlmEngine):
|
class HfLlmEngine(LlmEngine):
|
||||||
"""
|
"""
|
||||||
LLM Engine implementation using HuggingFace's InferenceClient.
|
LLM Engine implementation using HuggingFace's InferenceClient.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model: str,
|
model: str,
|
||||||
temperature: float,
|
temperature: float,
|
||||||
api_token: Optional[str],
|
api_token: Optional[str],
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Initialize the HuggingFace Inference API LLM Engine.
|
Initialize the HuggingFace Inference API LLM Engine.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
model: HuggingFace model ID to use
|
model: HuggingFace model ID to use
|
||||||
temperature: Sampling temperature
|
temperature: Sampling temperature
|
||||||
api_token: HuggingFace API token
|
api_token: HuggingFace API token
|
||||||
"""
|
"""
|
||||||
self._model = model
|
self._model = model
|
||||||
self._temperature = temperature
|
self._temperature = temperature
|
||||||
|
|
||||||
self._tokenizer = AutoTokenizer.from_pretrained(model, token=api_token)
|
self._tokenizer = AutoTokenizer.from_pretrained(model, token=api_token)
|
||||||
self._config = AutoConfig.from_pretrained(model, token=api_token)
|
self._config = AutoConfig.from_pretrained(model, token=api_token)
|
||||||
self._client = InferenceClient(token=api_token)
|
self._client = InferenceClient(token=api_token)
|
||||||
|
|
||||||
def infer(self, system_prompt: str, main_context: str, should_stop: Callable[[], bool] = lambda: False) -> Iterator[str]:
|
def infer(self, system_prompt: str, main_context: str, continuation_text: str, should_stop: Callable[[], bool] = lambda: False) -> Iterator[str]:
|
||||||
"""
|
"""
|
||||||
Run inference using the system prompt and main context.
|
Run inference using the system prompt and main context.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
system_prompt: The system prompt string
|
system_prompt: The system prompt string
|
||||||
main_context: The main context string after templating
|
main_context: The main context string after templating
|
||||||
should_stop: Callback that returns True when inference should stop
|
continuation_text: Part of the response that is already generated
|
||||||
|
should_stop: Callback that returns True when inference should stop
|
||||||
Returns:
|
|
||||||
Iterator[str]: An iterator that yields the generated text.
|
Returns:
|
||||||
"""
|
Iterator[str]: An iterator that yields the generated text.
|
||||||
messages = [
|
"""
|
||||||
{"role": "system", "content": system_prompt},
|
messages = [
|
||||||
{"role": "user", "content": main_context}
|
{"role": "system", "content": system_prompt},
|
||||||
]
|
{"role": "user", "content": main_context},
|
||||||
|
{"role": "assistant", "content": continuation_text},
|
||||||
stream = self._client.chat_completion(
|
]
|
||||||
model=self._model,
|
|
||||||
messages=messages,
|
stream = self._client.chat_completion(
|
||||||
temperature=self._temperature,
|
model=self._model,
|
||||||
stream=True
|
messages=messages,
|
||||||
)
|
temperature=self._temperature,
|
||||||
|
add_generation_prompt=False,
|
||||||
try:
|
stream=True
|
||||||
for response in stream:
|
)
|
||||||
if should_stop():
|
|
||||||
stream.close()
|
try:
|
||||||
break
|
for response in stream:
|
||||||
if content := response.choices[0].delta.content:
|
if should_stop():
|
||||||
yield content
|
stream.close()
|
||||||
finally:
|
break
|
||||||
stream.close()
|
if content := response.choices[0].delta.content:
|
||||||
|
yield content
|
||||||
def token_count(self, system_prompt: str, main_context: str) -> int:
|
finally:
|
||||||
messages = [
|
stream.close()
|
||||||
{"role": "system", "content": system_prompt},
|
|
||||||
{"role": "user", "content": main_context}
|
def token_count(self, system_prompt: str, main_context: str) -> int:
|
||||||
]
|
messages = [
|
||||||
prompt = self._tokenizer.apply_chat_template(
|
{"role": "system", "content": system_prompt},
|
||||||
messages, tokenize=False, add_generation_prompt=True
|
{"role": "user", "content": main_context}
|
||||||
)
|
]
|
||||||
return len(self._tokenizer.encode(prompt))
|
prompt = self._tokenizer.apply_chat_template(
|
||||||
|
messages, tokenize=False, add_generation_prompt=True
|
||||||
def token_limit(self) -> int:
|
)
|
||||||
"""
|
return len(self._tokenizer.encode(prompt))
|
||||||
Get the model's context window size.
|
|
||||||
|
def token_limit(self) -> int:
|
||||||
Returns:
|
"""
|
||||||
int: Maximum number of tokens the model can process
|
Get the model's context window size.
|
||||||
"""
|
|
||||||
return self._config.max_position_embeddings
|
Returns:
|
||||||
|
int: Maximum number of tokens the model can process
|
||||||
|
"""
|
||||||
|
return self._config.max_position_embeddings
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class LocalLlmEngine(LlmEngine):
|
|||||||
Args:
|
Args:
|
||||||
system_prompt: The system prompt string
|
system_prompt: The system prompt string
|
||||||
main_context: The main context string after templating
|
main_context: The main context string after templating
|
||||||
|
continuation_text: Part of the response that is already generated
|
||||||
should_stop: Callback that returns True when inference should stop
|
should_stop: Callback that returns True when inference should stop
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@@ -1,75 +1,77 @@
|
|||||||
from typing import Callable, Iterator
|
from typing import Callable, Iterator
|
||||||
import openai
|
import openai
|
||||||
import tiktoken
|
import tiktoken
|
||||||
|
|
||||||
from . import LlmEngine
|
from . import LlmEngine
|
||||||
|
|
||||||
class OpenAILlmEngine(LlmEngine):
|
class OpenAILlmEngine(LlmEngine):
|
||||||
"""
|
"""
|
||||||
LLM Engine implementation using OpenAI's API.
|
LLM Engine implementation using OpenAI's API.
|
||||||
Supports streaming responses from chat completion models.
|
Supports streaming responses from chat completion models.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model: str,
|
model: str,
|
||||||
temperature: float,
|
temperature: float,
|
||||||
token_limit: int,
|
token_limit: int,
|
||||||
api_key: str,
|
api_key: str,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Initialize the OpenAI LLM Engine.
|
Initialize the OpenAI LLM Engine.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
model: OpenAI model to use
|
model: OpenAI model to use
|
||||||
temperature: Temperature for sampling
|
temperature: Temperature for sampling
|
||||||
api_key: OpenAI API key
|
api_key: OpenAI API key
|
||||||
token_limit: Maximum number of tokens to generate
|
token_limit: Maximum number of tokens to generate
|
||||||
"""
|
"""
|
||||||
self._model = model
|
self._model = model
|
||||||
self._temperature = temperature
|
self._temperature = temperature
|
||||||
self._token_limit = token_limit
|
self._token_limit = token_limit
|
||||||
|
|
||||||
self._client = openai.Client(
|
self._client = openai.Client(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
def infer(self, system_prompt: str, main_context: str, should_stop: Callable[[], bool] = lambda: False) -> Iterator[str]:
|
def infer(self, system_prompt: str, main_context: str, continuation_text: str, should_stop: Callable[[], bool] = lambda: False) -> Iterator[str]:
|
||||||
messages = [
|
if continuation_text:
|
||||||
{"role": "system", "content": system_prompt},
|
print("OpenAI LLM Engine: continuation_text is not supported")
|
||||||
{"role": "user", "content": main_context}
|
messages = [
|
||||||
]
|
{"role": "system", "content": system_prompt},
|
||||||
|
{"role": "user", "content": main_context}
|
||||||
stream = self._client.chat.completions.create(
|
]
|
||||||
model=self._model,
|
|
||||||
messages=messages,
|
stream = self._client.chat.completions.create(
|
||||||
temperature=self._temperature,
|
model=self._model,
|
||||||
stream=True,
|
messages=messages,
|
||||||
)
|
temperature=self._temperature,
|
||||||
|
stream=True,
|
||||||
try:
|
)
|
||||||
for chunk in stream:
|
|
||||||
if should_stop():
|
try:
|
||||||
break
|
for chunk in stream:
|
||||||
if content := chunk.choices[0].delta.content:
|
if should_stop():
|
||||||
yield content
|
break
|
||||||
finally:
|
if content := chunk.choices[0].delta.content:
|
||||||
stream.close()
|
yield content
|
||||||
#stream.response.close()
|
finally:
|
||||||
|
stream.close()
|
||||||
def token_count(self, system_prompt: str, main_context: str) -> int:
|
#stream.response.close()
|
||||||
"""
|
|
||||||
Calculate the total token count for the system prompt and context.
|
def token_count(self, system_prompt: str, main_context: str) -> int:
|
||||||
|
"""
|
||||||
Args:
|
Calculate the total token count for the system prompt and context.
|
||||||
system_prompt: The system prompt string
|
|
||||||
main_context: The main context string
|
Args:
|
||||||
|
system_prompt: The system prompt string
|
||||||
Returns:
|
main_context: The main context string
|
||||||
int: Total number of tokens
|
|
||||||
"""
|
Returns:
|
||||||
encoding = tiktoken.encoding_for_model(self._model)
|
int: Total number of tokens
|
||||||
return len(encoding.encode(system_prompt)) + len(encoding.encode(main_context))
|
"""
|
||||||
|
encoding = tiktoken.encoding_for_model(self._model)
|
||||||
def token_limit(self) -> int:
|
return len(encoding.encode(system_prompt)) + len(encoding.encode(main_context))
|
||||||
|
|
||||||
|
def token_limit(self) -> int:
|
||||||
return self._token_limit
|
return self._token_limit
|
||||||
@@ -70,6 +70,7 @@ class QwQLlmEngine(LlmEngine):
|
|||||||
Args:
|
Args:
|
||||||
system_prompt: The system prompt string
|
system_prompt: The system prompt string
|
||||||
main_context: The main context string after templating
|
main_context: The main context string after templating
|
||||||
|
continuation_text: Part of the response that is already generated
|
||||||
should_stop: Callback that returns True when inference should stop
|
should_stop: Callback that returns True when inference should stop
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
164
sia/web/api.py
164
sia/web/api.py
@@ -59,20 +59,25 @@ class Api:
|
|||||||
|
|
||||||
async def _run_inference(self, request: web.Request) -> web.Response:
|
async def _run_inference(self, request: web.Request) -> web.Response:
|
||||||
"""Start inference on specified LLM."""
|
"""Start inference on specified LLM."""
|
||||||
llm_name = request.match_info["llm"]
|
|
||||||
try:
|
try:
|
||||||
|
llm_name = request.match_info["llm"]
|
||||||
|
data = await request.json()
|
||||||
|
text = data.get("response")
|
||||||
|
context = data.get("context")
|
||||||
|
self._agent._response_buffer.set_text(text)
|
||||||
|
self._agent.modify_context(context)
|
||||||
await asyncio.get_event_loop().run_in_executor(None, self._agent.run_inference, llm_name)
|
await asyncio.get_event_loop().run_in_executor(None, self._agent.run_inference, llm_name)
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except (ValueError, RuntimeError) as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _stop_inference(self, request: web.Request) -> web.Response:
|
async def _stop_inference(self, request: web.Request) -> web.Response:
|
||||||
"""Stop inference on specified LLM."""
|
"""Stop inference on specified LLM."""
|
||||||
llm_name = request.match_info["llm"]
|
|
||||||
try:
|
try:
|
||||||
|
llm_name = request.match_info["llm"]
|
||||||
self._agent.stop_inference(llm_name)
|
self._agent.stop_inference(llm_name)
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _set_response(self, request: web.Request) -> web.Response:
|
async def _set_response(self, request: web.Request) -> web.Response:
|
||||||
@@ -80,15 +85,10 @@ class Api:
|
|||||||
try:
|
try:
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
text = data.get("response")
|
text = data.get("response")
|
||||||
if text is None:
|
self._agent._response_buffer.set_text(text)
|
||||||
return web.Response(status=400, text="Missing response text in request body")
|
return web.Response(status=200)
|
||||||
try:
|
except Exception as e:
|
||||||
self._agent._response_buffer.set_text(text)
|
return web.Response(status=400, text=str(e))
|
||||||
return web.Response(status=200)
|
|
||||||
except ValueError as e:
|
|
||||||
return web.Response(status=400, text=str(e))
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
return web.Response(status=400, text="Invalid JSON in request body")
|
|
||||||
|
|
||||||
async def _approve_response(self, request: web.Request) -> web.Response:
|
async def _approve_response(self, request: web.Request) -> web.Response:
|
||||||
"""Approve current buffer content"""
|
"""Approve current buffer content"""
|
||||||
@@ -98,35 +98,37 @@ class Api:
|
|||||||
self._agent.response_buffer.set_text(response)
|
self._agent.response_buffer.set_text(response)
|
||||||
self._agent.approve_response()
|
self._agent.approve_response()
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _get_response(self, request: web.Request) -> web.Response:
|
async def _get_response(self, request: web.Request) -> web.Response:
|
||||||
"""Get current buffer content"""
|
"""Get current buffer content"""
|
||||||
return web.Response(
|
return web.Response(
|
||||||
text=json.dumps({
|
text=json.dumps({
|
||||||
"response": self._agent.output_buffer.get_text(),
|
"response": self._agent.response_buffer.get_text(),
|
||||||
}),
|
}),
|
||||||
content_type="application/json"
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _modify_context(self, request: web.Request) -> web.Response:
|
async def _modify_context(self, request: web.Request) -> web.Response:
|
||||||
"""Modify the current context."""
|
"""Modify the current context."""
|
||||||
data = await request.json()
|
try:
|
||||||
context = data.get("context")
|
data = await request.json()
|
||||||
if not context:
|
context = data.get("context")
|
||||||
return web.Response(status=400, text="Missing context in request body")
|
self._agent.modify_context(context)
|
||||||
self._agent.modify_context(context)
|
return web.Response(status=200)
|
||||||
return web.Response(status=200)
|
except Exception as e:
|
||||||
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _send_input(self, request: web.Request) -> web.Response:
|
async def _send_input(self, request: web.Request) -> web.Response:
|
||||||
"""Send input to the IO buffer."""
|
"""Send input to the IO buffer."""
|
||||||
data = await request.json()
|
try:
|
||||||
input_text = data.get("input")
|
data = await request.json()
|
||||||
if not input_text:
|
input_text = data.get("input")
|
||||||
return web.Response(status=400, text="Missing input in request body")
|
self._io_buffer.append_stdin(input_text)
|
||||||
self._io_buffer.append_stdin(input_text)
|
return web.Response(status=200)
|
||||||
return web.Response(status=200)
|
except Exception as e:
|
||||||
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _clear_output(self, request: web.Request) -> web.Response:
|
async def _clear_output(self, request: web.Request) -> web.Response:
|
||||||
"""Clear the stdout buffer."""
|
"""Clear the stdout buffer."""
|
||||||
@@ -135,14 +137,14 @@ class Api:
|
|||||||
|
|
||||||
async def _get_output(self, request: web.Request) -> web.Response:
|
async def _get_output(self, request: web.Request) -> web.Response:
|
||||||
"""Get complete output for specified LLM."""
|
"""Get complete output for specified LLM."""
|
||||||
llm_name = request.match_info["llm"]
|
|
||||||
try:
|
try:
|
||||||
|
llm_name = request.match_info["llm"]
|
||||||
output = self._agent.get_output(llm_name)
|
output = self._agent.get_output(llm_name)
|
||||||
return web.Response(
|
return web.Response(
|
||||||
text=json.dumps({"output": output}),
|
text=json.dumps({"output": output}),
|
||||||
content_type="application/json"
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _get_llms(self, request: web.Request) -> web.Response:
|
async def _get_llms(self, request: web.Request) -> web.Response:
|
||||||
@@ -165,71 +167,68 @@ class Api:
|
|||||||
|
|
||||||
async def _set_auto_approver_config(self, request: web.Request) -> web.Response:
|
async def _set_auto_approver_config(self, request: web.Request) -> web.Response:
|
||||||
"""Update auto approver configuration."""
|
"""Update auto approver configuration."""
|
||||||
data = await request.json()
|
|
||||||
try:
|
try:
|
||||||
|
data = await request.json()
|
||||||
self._auto_approver.set_config(data)
|
self._auto_approver.set_config(data)
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except (ValueError, KeyError) as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _set_context_enabled(self, request: web.Request) -> web.Response:
|
async def _set_context_enabled(self, request: web.Request) -> web.Response:
|
||||||
"""Set context auto-approval enabled state."""
|
"""Set context auto-approval enabled state."""
|
||||||
data = await request.json()
|
|
||||||
enabled = data.get("enabled")
|
|
||||||
if enabled is None:
|
|
||||||
return web.Response(status=400, text="Missing enabled parameter")
|
|
||||||
try:
|
try:
|
||||||
self._auto_approver.context_enabled = enabled
|
data = await request.json()
|
||||||
|
enabled = data.get("enabled")
|
||||||
|
self._auto_approver.context_enabled = enabled or False
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _set_response_enabled(self, request: web.Request) -> web.Response:
|
async def _set_response_enabled(self, request: web.Request) -> web.Response:
|
||||||
"""Set response auto-approval enabled state."""
|
"""Set response auto-approval enabled state."""
|
||||||
data = await request.json()
|
|
||||||
enabled = data.get("enabled")
|
|
||||||
if enabled is None:
|
|
||||||
return web.Response(status=400, text="Missing enabled parameter")
|
|
||||||
try:
|
try:
|
||||||
self._auto_approver.response_enabled = enabled
|
data = await request.json()
|
||||||
|
enabled = data.get("enabled")
|
||||||
|
self._auto_approver.response_enabled = enabled or False
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _set_context_timeout(self, request: web.Request) -> web.Response:
|
async def _set_context_timeout(self, request: web.Request) -> web.Response:
|
||||||
"""Set context auto-approval timeout."""
|
"""Set context auto-approval timeout."""
|
||||||
data = await request.json()
|
|
||||||
timeout = data.get("timeout")
|
|
||||||
if timeout is None:
|
|
||||||
return web.Response(status=400, text="Missing timeout parameter")
|
|
||||||
try:
|
try:
|
||||||
|
data = await request.json()
|
||||||
|
timeout = data.get("timeout")
|
||||||
|
if timeout is None:
|
||||||
|
return web.Response(status=400, text="Missing timeout parameter")
|
||||||
self._auto_approver.context_timeout = float(timeout)
|
self._auto_approver.context_timeout = float(timeout)
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
|
|
||||||
async def _set_response_timeout(self, request: web.Request) -> web.Response:
|
async def _set_response_timeout(self, request: web.Request) -> web.Response:
|
||||||
"""Set response auto-approval timeout."""
|
"""Set response auto-approval timeout."""
|
||||||
data = await request.json()
|
|
||||||
timeout = data.get("timeout")
|
|
||||||
if timeout is None:
|
|
||||||
return web.Response(status=400, text="Missing timeout parameter")
|
|
||||||
try:
|
try:
|
||||||
|
data = await request.json()
|
||||||
|
timeout = data.get("timeout")
|
||||||
|
if timeout is None:
|
||||||
|
return web.Response(status=400, text="Missing timeout parameter")
|
||||||
self._auto_approver.response_timeout = float(timeout)
|
self._auto_approver.response_timeout = float(timeout)
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _set_llm_name(self, request: web.Request) -> web.Response:
|
async def _set_llm_name(self, request: web.Request) -> web.Response:
|
||||||
"""Set LLM name for auto-approval."""
|
"""Set LLM name for auto-approval."""
|
||||||
data = await request.json()
|
|
||||||
name = data.get("name")
|
|
||||||
if name is None:
|
|
||||||
return web.Response(status=400, text="Missing name parameter")
|
|
||||||
try:
|
try:
|
||||||
|
data = await request.json()
|
||||||
|
name = data.get("name")
|
||||||
|
if name is None:
|
||||||
|
return web.Response(status=400, text="Missing name parameter")
|
||||||
self._auto_approver.llm_name = name
|
self._auto_approver.llm_name = name
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _get_memory(self, request: web.Request) -> web.Response:
|
async def _get_memory(self, request: web.Request) -> web.Response:
|
||||||
@@ -244,29 +243,29 @@ class Api:
|
|||||||
)
|
)
|
||||||
async def _create_entry(self, request: web.Request) -> web.Response:
|
async def _create_entry(self, request: web.Request) -> web.Response:
|
||||||
"""Create a new entry in working memory."""
|
"""Create a new entry in working memory."""
|
||||||
data = await request.json()
|
|
||||||
try:
|
try:
|
||||||
|
data = await request.json()
|
||||||
entry = EntryFactory.create_entry(data, self._work_dir, self._io_buffer)
|
entry = EntryFactory.create_entry(data, self._work_dir, self._io_buffer)
|
||||||
self._working_memory.add_entry(entry)
|
self._working_memory.add_entry(entry)
|
||||||
return web.Response(
|
return web.Response(
|
||||||
text=json.dumps({"id": entry.id}),
|
text=json.dumps({"id": entry.id}),
|
||||||
content_type="application/json"
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
except (ValueError, TypeError) as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _save_entry(self, request: web.Request) -> web.Response:
|
async def _save_entry(self, request: web.Request) -> web.Response:
|
||||||
"""Update properties of an existing entry."""
|
"""Update properties of an existing entry."""
|
||||||
entry_id = request.match_info["id"]
|
|
||||||
data = await request.json()
|
|
||||||
entry = self._working_memory.get_entry(entry_id)
|
|
||||||
if not entry:
|
|
||||||
return web.Response(status=404, text="Entry not found")
|
|
||||||
try:
|
try:
|
||||||
|
entry_id = request.match_info["id"]
|
||||||
|
data = await request.json()
|
||||||
|
entry = self._working_memory.get_entry(entry_id)
|
||||||
|
if not entry:
|
||||||
|
return web.Response(status=404, text="Entry not found")
|
||||||
EntryFactory.update_entry(entry, data)
|
EntryFactory.update_entry(entry, data)
|
||||||
entry.notify_change()
|
entry.notify_change()
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _delete_entry(self, request: web.Request) -> web.Response:
|
async def _delete_entry(self, request: web.Request) -> web.Response:
|
||||||
@@ -286,27 +285,30 @@ class Api:
|
|||||||
|
|
||||||
async def _update_entry(self, request: web.Request) -> web.Response:
|
async def _update_entry(self, request: web.Request) -> web.Response:
|
||||||
"""Update an entry's state."""
|
"""Update an entry's state."""
|
||||||
entry_id = request.match_info["id"]
|
|
||||||
entry = self._working_memory.get_entry(entry_id)
|
|
||||||
if not entry:
|
|
||||||
return web.Response(status=404, text="Entry not found")
|
|
||||||
try:
|
try:
|
||||||
|
entry_id = request.match_info["id"]
|
||||||
|
entry = self._working_memory.get_entry(entry_id)
|
||||||
|
if not entry:
|
||||||
|
return web.Response(status=404, text="Entry not found")
|
||||||
entry.update()
|
entry.update()
|
||||||
entry.notify_change()
|
entry.notify_change()
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
return web.Response(status=400, text=str(e))
|
return web.Response(status=400, text=str(e))
|
||||||
|
|
||||||
async def _load_iteration(self, request: web.Request) -> web.Response:
|
async def _load_iteration(self, request: web.Request) -> web.Response:
|
||||||
"""Load entries from iteration XML content into working memory"""
|
"""Load entries from iteration XML content into working memory"""
|
||||||
data = await request.json()
|
try:
|
||||||
content = data.get("content")
|
data = await request.json()
|
||||||
if not content:
|
content = data.get("content")
|
||||||
return web.Response(status=400, text="Missing content in request body")
|
if not content:
|
||||||
|
return web.Response(status=400, text="Missing content in request body")
|
||||||
|
|
||||||
|
entries = IterationParser.parse_iteration(content, self._work_dir, self._io_buffer)
|
||||||
|
|
||||||
entries = IterationParser.parse_iteration(content, self._work_dir, self._io_buffer)
|
for entry in entries:
|
||||||
|
self._working_memory.add_entry(entry)
|
||||||
for entry in entries:
|
|
||||||
self._working_memory.add_entry(entry)
|
return web.Response(status=200)
|
||||||
|
except Exception as e:
|
||||||
return web.Response(status=200)
|
return web.Response(status=400, text=str(e))
|
||||||
|
|||||||
@@ -97,10 +97,6 @@ class WebAgent(BaseAgent):
|
|||||||
"""Update context and reset all LLM states"""
|
"""Update context and reset all LLM states"""
|
||||||
with self._llm_lock:
|
with self._llm_lock:
|
||||||
self._context = context
|
self._context = context
|
||||||
self._response_buffer.clear()
|
|
||||||
for llm_name in self._llms:
|
|
||||||
self._set_llm_state(llm_name, LlmState.IDLE)
|
|
||||||
|
|
||||||
for handler in self._context_change_handlers:
|
for handler in self._context_change_handlers:
|
||||||
handler(context, generated)
|
handler(context, generated)
|
||||||
|
|
||||||
@@ -118,7 +114,6 @@ class WebAgent(BaseAgent):
|
|||||||
return self._stop_flags[llm_name]
|
return self._stop_flags[llm_name]
|
||||||
response_token_iter = llm.infer(self.system_prompt, self.context, self._response_buffer.get_text(), should_stop)
|
response_token_iter = llm.infer(self.system_prompt, self.context, self._response_buffer.get_text(), should_stop)
|
||||||
for token in response_token_iter:
|
for token in response_token_iter:
|
||||||
print(token, end='', flush=True)
|
|
||||||
with self._output_lock:
|
with self._output_lock:
|
||||||
self._response_buffer.append_text(token)
|
self._response_buffer.append_text(token)
|
||||||
with self._llm_lock:
|
with self._llm_lock:
|
||||||
@@ -132,11 +127,10 @@ class WebAgent(BaseAgent):
|
|||||||
|
|
||||||
def approve_response(self) -> None:
|
def approve_response(self) -> None:
|
||||||
"""Process approved response from specified LLM"""
|
"""Process approved response from specified LLM"""
|
||||||
if self.llms.get(llm_name) != LlmState.IDLE:
|
|
||||||
return
|
|
||||||
timestamp = datetime.now(timezone.utc)
|
timestamp = datetime.now(timezone.utc)
|
||||||
self._iteration_logger.log_iteration(timestamp, self._context, self._response_buffer.get_text())
|
self._iteration_logger.log_iteration(timestamp, self._context, self._response_buffer.get_text())
|
||||||
parse_result = self._parser.parse(timestamp, self._response_buffer.get_text())
|
parse_result = self._parser.parse(timestamp, self._response_buffer.get_text())
|
||||||
|
self._response_buffer.clear()
|
||||||
if isinstance(parse_result, Command):
|
if isinstance(parse_result, Command):
|
||||||
result = parse_result.execute(self._working_memory)
|
result = parse_result.execute(self._working_memory)
|
||||||
self._command_result = result
|
self._command_result = result
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const App = () => {
|
|||||||
// Editor content state
|
// Editor content state
|
||||||
const [generatedContext, setGeneratedContext] = useState('');
|
const [generatedContext, setGeneratedContext] = useState('');
|
||||||
const [modifiedContext, setModifiedContext] = useState('');
|
const [modifiedContext, setModifiedContext] = useState('');
|
||||||
const [contextDirty, setContextDirty] = useState(false);
|
|
||||||
const [generatedResponse, setGeneratedResponse] = useState('');
|
const [generatedResponse, setGeneratedResponse] = useState('');
|
||||||
const [modifiedResponse, setModifiedResponse] = useState('');
|
const [modifiedResponse, setModifiedResponse] = useState('');
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
@@ -65,7 +64,6 @@ const App = () => {
|
|||||||
// Handle context changes
|
// Handle context changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
contextWs.addMessageHandler((data) => {
|
contextWs.addMessageHandler((data) => {
|
||||||
setContextDirty(false);
|
|
||||||
setModifiedContext(data.context);
|
setModifiedContext(data.context);
|
||||||
if (data.generated) {
|
if (data.generated) {
|
||||||
setGeneratedContext(data.context);
|
setGeneratedContext(data.context);
|
||||||
@@ -127,20 +125,13 @@ const App = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleInference = () => {
|
const handleInference = () => {
|
||||||
fetch('/api/context', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ context: modifiedContext })
|
|
||||||
});
|
|
||||||
|
|
||||||
fetch('/api/response', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ response: modifiedResponse })
|
|
||||||
});
|
|
||||||
|
|
||||||
fetch(`/api/inference/${activeLlm}`, {
|
fetch(`/api/inference/${activeLlm}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
response: modifiedResponse,
|
||||||
|
context: modifiedContext,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -176,10 +167,6 @@ const App = () => {
|
|||||||
}
|
}
|
||||||
setLlms(resetLlms);
|
setLlms(resetLlms);
|
||||||
setModifiedContext(context);
|
setModifiedContext(context);
|
||||||
|
|
||||||
// Reset response buffers
|
|
||||||
setGeneratedResponse('');
|
|
||||||
setModifiedResponse('');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResponseEdit = (response) => {
|
const handleResponseEdit = (response) => {
|
||||||
|
|||||||
@@ -4,58 +4,58 @@ import { Card, CardContent } from '@/components/ui/card';
|
|||||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||||
|
|
||||||
export const StandardEditor = ({
|
export const StandardEditor = ({
|
||||||
content,
|
content,
|
||||||
onChange,
|
onChange,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
language = 'xml',
|
language = 'xml',
|
||||||
validationError = null,
|
validationError = null,
|
||||||
autoScroll = false,
|
autoScroll = false,
|
||||||
}) => {
|
}) => {
|
||||||
const editorRef = React.useRef(null);
|
const editorRef = React.useRef(null);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
if (editorRef.current && autoScroll) {
|
if (editorRef.current && autoScroll) {
|
||||||
const model = editorRef.current.getModel();
|
const model = editorRef.current.getModel();
|
||||||
const lineCount = model.getLineCount();
|
const lineCount = model.getLineCount();
|
||||||
editorRef.current.revealLine(lineCount, monaco.editor.ScrollType.Smooth);
|
editorRef.current.revealLine(lineCount, monaco.editor.ScrollType.Smooth);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditorDidMount = (editor) => {
|
const handleEditorDidMount = (editor) => {
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, 10);
|
}, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [content, autoScroll]);
|
}, [content, autoScroll]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="h-[calc(100vh-8rem)]">
|
<Card className="h-[calc(100vh-8rem)]">
|
||||||
<CardContent className="p-0 h-full">
|
<CardContent className="p-0 h-full">
|
||||||
{validationError && (
|
{validationError && (
|
||||||
<Alert variant="destructive" className="m-4">
|
<Alert variant="destructive" className="m-4">
|
||||||
<AlertDescription>{validationError}</AlertDescription>
|
<AlertDescription>{validationError}</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<Editor
|
<Editor
|
||||||
height="100%"
|
height="100%"
|
||||||
language={language}
|
language={language}
|
||||||
value={content}
|
value={content}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onMount={handleEditorDidMount}
|
onMount={handleEditorDidMount}
|
||||||
options={{
|
options={{
|
||||||
minimap: { enabled: false },
|
minimap: { enabled: false },
|
||||||
lineNumbers: 'on',
|
lineNumbers: 'on',
|
||||||
readOnly,
|
readOnly,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
automaticLayout: true,
|
automaticLayout: true,
|
||||||
wordWrap: 'on',
|
wordWrap: 'on',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user