Skip to content

Commit 296e635

Browse files
committed
0.1.1-alpha: better arg parsing, make guided generation optional (disabled by default), verbose mode
1 parent a5490a3 commit 296e635

File tree

12 files changed

+110
-70
lines changed

12 files changed

+110
-70
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,10 @@ CoreAgent makes it easy to integrate your own custom functionalities as tools. T
6262
6363
Refer to the example above for a practical demonstration of tool registration.
6464
65-
## Limitations
66-
67-
Currently, it relies on `guided_grammar` function from `vLLM` to function correctly, so you have to use `vLLM` as inference server.
6865
6966
## Roadmap
7067
- [x] Basic universal agent framework.
71-
- [ ] Remove `guided_grammar` restriction, allow general LLM usage (DeepSeek API, GPT3.5/4/4o API, Qwen API, etc. )
68+
- [x] Remove `guided_grammar` restriction, allow general LLM usage (DeepSeek API, GPT3.5/4/4o API, Qwen API, etc. )
7269
- [ ] More intuitive simplified examples, alowing hands-on try-outs.
7370
- [ ] RAG-based memory module.
7471

README.zh.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ CoreAgent 可以轻松地将您自己的自定义功能集成为工具。要注
6161

6262
请参阅上面的示例,以获取工具注册的实际演示。
6363

64-
## 使用限制
65-
66-
目前,该项目依赖`vLLM``guided_grammar`功能运行,所以你使用`vLLM`git status作为推理服务。
64+
## Roadmap
65+
- [x] 基础框架。
66+
- [x] 移除 `guided_grammar` 强制需求,支持常用LLM API (DeepSeek API, GPT3.5/4/4o API, Qwen API, etc. )
67+
- [ ] 更简单的样例代码。
68+
- [ ] 基于RAG的记忆系统。
6769

6870
## 贡献
6971

coreagent/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from .config import Config, get_default_config, set_default_config
22
from .agent import Agent, Identity
3+
from .arg_parsers import set_default_config_from_args

coreagent/agent.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,18 @@ def _call_llm(self, history) -> str:
157157
# Executes a single turn of LLM call.
158158
history: "Chat history [{\"role\": ..., \"content\": ...}, ...]"
159159
"""
160-
grammar_text = generate_aiml_syntax(self.identity.respond_gbnf, dict(
161-
[(x, self.tool_desc[x].param_names) for x in self.tool_desc]
162-
))
163-
extra_body = dict(
164-
guided_grammar=grammar_text,
165-
guided_decoding_backend=self.config.guided_decoding_backend,
166-
)
167-
if self.config.chat_template_type in chat_templates:
160+
extra_body: dict = {}
161+
162+
if self.config.use_guided_generation:
163+
grammar_text = generate_aiml_syntax(self.identity.respond_gbnf, dict(
164+
[(x, self.tool_desc[x].param_names) for x in self.tool_desc]
165+
))
166+
extra_body = dict(
167+
guided_grammar=grammar_text,
168+
guided_decoding_backend=self.config.guided_decoding_backend,
169+
)
170+
171+
if self.config.chat_template_type is not None and self.config.chat_template_type in chat_templates:
168172
extra_body['chat_template'] = chat_templates[self.config.chat_template_type],
169173
if not self.config.show_generation:
170174
r = self.config.llm.chat.completions.create(
@@ -180,29 +184,29 @@ def _call_llm(self, history) -> str:
180184
print(r.choices[0].message)
181185
print(f'WARNING: finish_reason={r.choices[0].finish_reason}')
182186
raise Exception("too long")
183-
if 'content' not in r.choices[0].message or len(r.choices[0].message.content) <= 0:
187+
if r.choices[0].message is None or len(r.choices[0].message.content) <= 0:
184188
print(r.choices[0])
185189
raise Exception("empty LLM response")
186190
return r.choices[0].message.content
187191
##########
188192
r = self.config.llm.chat.completions.create(
189193
model=self.config.model,
190194
messages=history,
191-
stream=True,
192-
temperature=0.0,
195+
temperature=self.config.temperature,
193196
extra_body=extra_body,
194-
frequency_penalty=self.identity.frequency_penalty,
195-
max_completion_tokens=self.identity.generation_limit,
197+
frequency_penalty=self.config.frequency_penalty,
198+
max_completion_tokens=self.config.generation_limit,
196199
stop="\n$$EOF$$" if self.config.use_stop_token else None,
200+
stream=True
197201
)
198202
total = ''
199203
reasoning = ''
200204
resp = ''
201-
# prog = tqdm(r, unit='')
205+
prog = tqdm(r, unit='')
202206
finish_reason = None
203207

204208
entered_content = False
205-
for chunk in r:
209+
for chunk in prog:
206210
# print(chunk.choices[0], flush=True)
207211
if hasattr(chunk.choices[0].delta, "reasoning_content"):
208212
total += chunk.choices[0].delta.reasoning_content
@@ -211,13 +215,13 @@ def _call_llm(self, history) -> str:
211215
elif hasattr(chunk.choices[0].delta, "content") and len(chunk.choices[0].delta.content) > 0:
212216
if not entered_content:
213217
entered_content=True
214-
print("\n========\nOUTPUT: \n")
218+
# print("\n========\nOUTPUT: \n")
215219
total += chunk.choices[0].delta.content
216220
resp += chunk.choices[0].delta.content
217-
print(chunk.choices[0].delta.content, end='', flush=True)
221+
# print(chunk.choices[0].delta.content, end='', flush=True)
218222
if len(total) > self.config.progressbar_length:
219223
total = total[-self.config.progressbar_length:]
220-
# prog.set_postfix_str(total.replace("\n", ""), refresh=False)
224+
prog.set_postfix_str(total.replace("\n", "").replace("\r", ""), refresh=False)
221225
finish_reason = chunk.choices[0].finish_reason
222226
if finish_reason == 'length':
223227
raise Exception('generation too long')

coreagent/arg_parsers.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from argparse import ArgumentParser
2+
from typing import Sequence, Optional
3+
from collections.abc import Callable
4+
5+
import openai
6+
7+
from coreagent import set_default_config, Config
8+
9+
def set_default_config_from_args(args: Sequence[str] | None = None, argument_parser_handler: Optional[Callable[[ArgumentParser], None]] = None):
10+
"""
11+
Set default configuration from command-line arguments.
12+
:param args: Where to parse from? Set to None to use command-line arguments.
13+
:param argument_parser_handler: In case you want to get extra params.
14+
:return: Parsed parameters.
15+
"""
16+
arg_parser = ArgumentParser()
17+
arg_parser.add_argument("--guided", "-g", action="store_true", default=False, help="Use xgrammar guided generation. ")
18+
arg_parser.add_argument("--api-base-url", "-u", default='http://192.168.1.5:9900/v1/', help="OpenAI-Compatible API base url. ")
19+
arg_parser.add_argument("--api-key", "-k", default="1", help="API key ")
20+
arg_parser.add_argument("--model", "-m", default="llm", help="Model to use. ")
21+
arg_parser.add_argument("--verbose", "-v", action="store_true", default=False, help="Show generation process via a progress bar. ")
22+
23+
if argument_parser_handler is not None:
24+
argument_parser_handler(arg_parser)
25+
26+
args = arg_parser.parse_args(args)
27+
28+
if args.api_base_url is None:
29+
args.api_base_url = None
30+
31+
if args.verbose:
32+
print("[Verbose] Showing generation process via a progress bar. ")
33+
34+
if args.guided:
35+
print("[Guided] Using guided generation (xgrammar). ")
36+
37+
cli = openai.Client(
38+
base_url=args.api_base_url,
39+
api_key=args.api_key,
40+
)
41+
set_default_config(Config(cli, args.model, use_guided_generation=args.guided, show_generation=args.verbose))
42+
43+
return args

coreagent/config.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ class Config:
1515
temperature: Optional[float] = None
1616
frequency_penalty: float = None # generally don't set this, may cause problems.
1717
generation_limit: int = 5000
18-
show_generation: bool = False # Don't use it for now, a bug in vLLM (tested as of <= v0.8.0) caused random junks to be streamed, check out vLLM Issue #15188.
1918
# ---- optional settings ----
20-
progressbar_length: int = 50 # Not used for now
19+
use_guided_generation: bool = False # Disable if you're using non vLLM deployments
2120
guided_decoding_backend: str = 'xgrammar:no-fallback' # Tested with vLLM with Engine v0.
2221
use_stop_token: bool = False # Tested not working with vLLM <= 0.8.0, since stop tokens are also considered during reasoning, see vLLM Issue #14170.
2322
chat_template_type: Optional[str] = None # modified chat templates, only for vLLM, one of ["qwq" or None]
23+
# ---- display only ----
24+
show_generation: bool = False # Don't use it for now, a bug in vLLM (tested as of <= v0.8.0) caused random junks to be streamed, check out vLLM Issue #15188.
25+
progressbar_length: int = 50 # Not used for now
2426

2527
# Default configuration (used internally, do NOT modify directly! )
2628
default_config: Optional[Config] = None

examples/bomber.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import openai
2-
from coreagent import Agent, Config, set_default_config
1+
from coreagent import Agent, set_default_config_from_args, get_default_config
2+
3+
# load deafult configurations from command-line arguments
4+
set_default_config_from_args()
35

46
class Bomber:
57
def __init__(self):
@@ -14,11 +16,8 @@ class Killer:
1416
def kill(self, name: str):
1517
return f"Update: {name} is now killed! "
1618

17-
cli = openai.Client(
18-
base_url='http://192.168.1.5:9900/v1/',
19-
api_key='1',
20-
)
21-
set_default_config(Config(cli, "llm"))
19+
if get_default_config().use_guided_generation:
20+
print("Using xgrammar guided generation. ")
2221

2322
s = Agent()
2423
s.register_tool(Bomber())

examples/filetool.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1+
from argparse import ArgumentParser
2+
13
import openai
2-
from coreagent import Agent, Config, set_default_config, Identity
4+
from coreagent import Agent, Config, set_default_config_from_args, get_default_config
35
from coreagent.builtin import FileTool
46

5-
cli = openai.Client(
6-
base_url='http://192.168.1.5:9900/v1/',
7-
api_key='1',
8-
)
9-
set_default_config(Config(cli, "llm"))
7+
# We have extra arguments.
8+
def register_extra_args(ap: ArgumentParser):
9+
ap.add_argument('--root-dir', '-d', default='.', type=str)
10+
ap.add_argument('--allow-write', '-w', default=False, type=bool)
11+
12+
# load deafult configurations from command-line arguments
13+
args = set_default_config_from_args(argument_parser_handler=register_extra_args)
1014

11-
import argparse
12-
ap = argparse.ArgumentParser()
13-
ap.add_argument('--root-dir', '-d', default='.', type=str)
14-
ap.add_argument('--allow-write', '-w', default=False, type=bool)
15-
args = ap.parse_args()
15+
# update some required params since we might generate a lot!
16+
default_config: Config = get_default_config()
17+
default_config.generation_limit=5000
1618

17-
s = Agent(Identity(show_generation=False, generation_limit=5000, temperature=0.0))
19+
s = Agent()
1820
s.register_tool(FileTool(args.root_dir), exclude=['write_file', 'mkdir'] if not args.allow_write else None)
1921

2022
while True:

examples/guard_and_thief.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
import os
1+
from coreagent import Agent, Identity, set_default_config_from_args
22

3-
import openai
4-
from coreagent import Agent, Identity, Config, set_default_config
5-
6-
cli = openai.Client(
7-
base_url='http://192.168.1.5:9900/v1/',
8-
api_key='1',
9-
)
10-
set_default_config(Config(cli, "llm"))
3+
# load deafult configurations from command-line arguments
4+
set_default_config_from_args()
115

126
class TortureState:
137
def __init__(self):

examples/toolgen.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import openai
2-
from coreagent import Agent, Config, set_default_config, Identity
2+
from coreagent import Agent, Identity, set_default_config_from_args
33
from coreagent.builtin import ToolGen
44

5-
cli = openai.Client(
6-
base_url='http://192.168.1.5:9900/v1/',
7-
api_key='1',
8-
)
9-
set_default_config(Config(cli, "llm"))
5+
# load deafult configurations from command-line arguments
6+
set_default_config_from_args()
107

11-
s = Agent(Identity(show_generation=False, generation_limit=5000, temperature=0.0))
8+
9+
10+
s = Agent()
1211
s.register_tool(ToolGen(s))
1312

1413
while True:

0 commit comments

Comments
 (0)