-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Agent, Action, Observation Abstraction and with updated Controller #105
Changes from 38 commits
d3c0ff6
42675f2
a1fc2c4
d99ee8a
e602bfb
ae677e8
37a8d9a
0027cb5
6bca774
a7707c3
f974b8d
45c948c
0e2d969
9a3368b
819df04
723825b
01ddb5a
a746390
2c57973
3e76c67
e912823
1761175
4f22b3d
5073240
b05ede0
fcee6af
87810fa
ba81d12
40501e3
ab585ad
14bbbb3
1f479ef
8ca8547
6ef558d
5c1c762
43440c6
ae90a7b
0e9c7f3
2e343ee
2a7a4d7
0d86b2e
2ecc981
fb10f2d
c405f21
932fb38
ac9378d
e6106fa
e48c291
c184e4e
bb36f15
d3807dd
787a012
c1e386e
9e07ec0
0d204e2
ff48ea0
9829de5
1fe6557
9a1b087
c5c0a0f
adf5edd
6c78ae0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,28 @@ | ||
from typing import List | ||
from typing import List, Dict, Type | ||
|
||
import agenthub.langchains_agent.utils.llm as llm | ||
from opendevin.agent import Agent | ||
from opendevin.action import ( | ||
Action, | ||
CmdRunAction, | ||
CmdKillAction, | ||
BrowseURLAction, | ||
FileReadAction, | ||
FileWriteAction, | ||
AgentRecallAction, | ||
AgentThinkAction, | ||
AgentFinishAction, | ||
) | ||
from opendevin.observation import ( | ||
Observation, | ||
CmdOutputObservation, | ||
BrowserOutputObservation, | ||
) | ||
from opendevin.state import State | ||
|
||
from agenthub.langchains_agent.utils.monologue import Monologue | ||
from agenthub.langchains_agent.utils.memory import LongTermMemory | ||
|
||
from agenthub.langchains_agent.utils.agent import Agent as LangchainsAgentImpl | ||
from opendevin.lib.event import Event | ||
|
||
INITIAL_THOUGHTS = [ | ||
"I exist!", | ||
|
@@ -44,55 +63,131 @@ | |
] | ||
|
||
|
||
MAX_OUTPUT_LENGTH = 5000 | ||
MAX_MONOLOGUE_LENGTH = 20000 | ||
|
||
|
||
ACTION_TYPE_TO_CLASS: Dict[str, Type[Action]] = { | ||
"run": CmdRunAction, | ||
"kill": CmdKillAction, | ||
"browse": BrowseURLAction, | ||
"read": FileReadAction, | ||
"write": FileWriteAction, | ||
"recall": AgentRecallAction, | ||
"think": AgentThinkAction, | ||
"finish": AgentFinishAction, | ||
} | ||
|
||
CLASS_TO_ACTION_TYPE: Dict[Type[Action], str] = {v: k for k, v in ACTION_TYPE_TO_CLASS.items()} | ||
|
||
class LangchainsAgent(Agent): | ||
_initialized = False | ||
|
||
def __init__(self, instruction: str, model_name: str): | ||
super().__init__(instruction, model_name) | ||
self.monologue = Monologue(self.model_name) | ||
self.memory = LongTermMemory() | ||
|
||
def _add_event(self, event: dict): | ||
if 'output' in event['args']: | ||
event['args']['output'] = event['args']['output'][:MAX_OUTPUT_LENGTH] + "..." | ||
|
||
self.monologue.add_event(event) | ||
self.memory.add_event(event) | ||
if self.monologue.get_total_length() > MAX_MONOLOGUE_LENGTH: | ||
self.monologue.condense() | ||
|
||
def _initialize(self): | ||
if self._initialized: | ||
return | ||
self.agent = LangchainsAgentImpl(self.instruction, self.model_name) | ||
next_is_output = False | ||
for thought in INITIAL_THOUGHTS: | ||
thought = thought.replace("$TASK", self.instruction) | ||
if next_is_output: | ||
event = Event("output", {"output": thought}) | ||
d = {"action": "output", "args": {"output": thought}} | ||
next_is_output = False | ||
else: | ||
if thought.startswith("RUN"): | ||
command = thought.split("RUN ")[1] | ||
event = Event("run", {"command": command}) | ||
d = {"action": "run", "args": {"command": command}} | ||
next_is_output = True | ||
|
||
elif thought.startswith("RECALL"): | ||
query = thought.split("RECALL ")[1] | ||
event = Event("recall", {"query": query}) | ||
d = {"action": "recall", "args": {"query": query}} | ||
next_is_output = True | ||
|
||
elif thought.startswith("BROWSE"): | ||
url = thought.split("BROWSE ")[1] | ||
event = Event("browse", {"url": url}) | ||
d = {"action": "browse", "args": {"url": url}} | ||
next_is_output = True | ||
else: | ||
event = Event("think", {"thought": thought}) | ||
self.agent.add_event(event) | ||
self._initialized = True | ||
d = {"action": "think", "args": {"thought": thought}} | ||
|
||
def add_event(self, event: Event) -> None: | ||
self.agent.add_event(event) | ||
self._add_event(d) | ||
self._initialized = True | ||
|
||
def step(self, cmd_mgr) -> Event: | ||
def step(self, state: State) -> Action: | ||
self._initialize() | ||
return self.agent.get_next_action(cmd_mgr) | ||
# TODO: make langchains agent use Action & Observation | ||
# completly from ground up | ||
|
||
def search_memory(self, query: str) -> List[str]: | ||
return self.agent.memory.search(query) | ||
# Translate state to action_dict | ||
for info in state.updated_info: | ||
if isinstance(info, Observation): | ||
if isinstance(info, CmdOutputObservation): | ||
if info.error: | ||
d = {"action": "error", "args": {"output": info.content}} | ||
else: | ||
d = {"action": "output", "args": {"output": info.content}} | ||
# elif isinstance(info, UserMessageObservation): | ||
# d = {"action": "output", "args": {"output": info.message}} | ||
# elif isinstance(info, AgentMessageObservation): | ||
# d = {"action": "output", "args": {"output": info.message}} | ||
elif isinstance(info, BrowserOutputObservation): | ||
d = {"action": "output", "args": {"output": info.content}} | ||
else: | ||
raise NotImplementedError(f"Unknown observation type: {info}") | ||
self._add_event(d) | ||
elif isinstance(info, Action): | ||
if isinstance(info, CmdRunAction): | ||
d = {"action": "run", "args": {"command": info.command}} | ||
elif isinstance(info, CmdKillAction): | ||
d = {"action": "kill", "args": {"id": info.id}} | ||
elif isinstance(info, BrowseURLAction): | ||
d = {"action": "browse", "args": {"url": info.url}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Action <-> JSON conversion is going to be very important, so it's probably something we want to abstract. For one, the server and client are going to need to send Actions and Observations back and forth, and will need to serialize them. Ditto for agent <-> LLM interactions. Could There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so! But I feel we should probably prioritize getting this abstraction merged, then start a separate PR to re-write the inner workings of |
||
elif isinstance(info, FileReadAction): | ||
d = {"action": "read", "args": {"file": info.path}} | ||
elif isinstance(info, FileWriteAction): | ||
d = {"action": "write", "args": {"file": info.path, "content": info.contents}} | ||
elif isinstance(info, AgentRecallAction): | ||
d = {"action": "recall", "args": {"query": info.query}} | ||
elif isinstance(info, AgentThinkAction): | ||
d = {"action": "think", "args": {"thought": info.thought}} | ||
elif isinstance(info, AgentFinishAction): | ||
d = {"action": "finish"} | ||
else: | ||
raise NotImplementedError(f"Unknown action type: {info}") | ||
self._add_event(d) | ||
|
||
state.updated_info = [] | ||
|
||
action_dict = llm.request_action( | ||
self.instruction, | ||
self.monologue.get_thoughts(), | ||
self.model_name, | ||
state.background_commands_obs, | ||
) | ||
if action_dict is None: | ||
action_dict = {"action": "think", "args": {"thought": "..."}} | ||
|
||
def chat(self, message: str) -> None: | ||
""" | ||
Optional method for interactive communication with the agent during its execution. Implementations | ||
can use this method to modify the agent's behavior or state based on chat inputs. | ||
# Translate action_dict to Action | ||
action = ACTION_TYPE_TO_CLASS[action_dict["action"]](**action_dict["args"]) | ||
self.latest_action = action | ||
return action | ||
|
||
def search_memory(self, query: str) -> List[str]: | ||
return self.memory.search(query) | ||
|
||
Parameters: | ||
- message (str): The chat message or command. | ||
""" | ||
raise NotImplementedError | ||
|
||
Agent.register("LangchainsAgent", LangchainsAgent) |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the old LangchainsAgentImpl and directly integrate it into one class for simplicity.