yaicli 0.6.4__py3-none-any.whl → 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
yaicli/utils.py CHANGED
@@ -1,4 +1,6 @@
1
+ import asyncio
1
2
  import platform
3
+ from functools import wraps
2
4
  from os import getenv
3
5
  from os.path import basename, pathsep
4
6
  from typing import Any, Callable, Optional, TypeVar
@@ -132,3 +134,35 @@ def str2bool(value: Any) -> bool:
132
134
 
133
135
  # Handle empty strings and other invalid values
134
136
  raise ValueError(f"Invalid boolean value: {value}")
137
+
138
+
139
+ def get_or_create_event_loop() -> asyncio.AbstractEventLoop:
140
+ """
141
+ Get the current event loop or create a new one if it doesn't exist.
142
+ Compatible with Python 3.10+.
143
+
144
+ Returns:
145
+ asyncio.AbstractEventLoop: The current event loop or a new one if it doesn't exist.
146
+ """
147
+ try:
148
+ # Try to get the current running event loop
149
+ return asyncio.get_running_loop()
150
+ except RuntimeError:
151
+ # No running event loop, get or create event loop
152
+ try:
153
+ return asyncio.get_event_loop()
154
+ except RuntimeError:
155
+ # Create a new event loop and set it as the current thread's event loop
156
+ loop = asyncio.new_event_loop()
157
+ asyncio.set_event_loop(loop)
158
+ return loop
159
+
160
+
161
+ def wrap_function(func: Callable) -> Callable:
162
+ """Wrap a function to add a name and docstring"""
163
+
164
+ @wraps(func)
165
+ def wrapper(*args, **kwargs):
166
+ return func(*args, **kwargs)
167
+
168
+ return wrapper
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yaicli
3
- Version: 0.6.4
3
+ Version: 0.7.0
4
4
  Summary: A simple CLI tool to interact with LLM
5
5
  Project-URL: Homepage, https://github.com/belingud/yaicli
6
6
  Project-URL: Repository, https://github.com/belingud/yaicli
@@ -265,6 +265,7 @@ generate and execute shell commands, or get quick answers without leaving your w
265
265
  > [!NOTE]
266
266
  > YAICLI is actively developed. While core functionality is stable, some features may evolve in future releases.
267
267
 
268
+ > We support MCP since v0.7.0!
268
269
  > We support Function Call since v0.5.0!
269
270
 
270
271
  ## ✨ Key Features
@@ -362,7 +363,6 @@ pip install .
362
363
  - Deepseek
363
364
  - Doubao
364
365
  - Gemini
365
- - Vertex AI
366
366
  - Groq
367
367
  - Huggingface
368
368
  - Minimax
@@ -372,6 +372,7 @@ pip install .
372
372
  - Sambanova
373
373
  - Siliconflow
374
374
  - Targon
375
+ - Vertex ai
375
376
  - X AI
376
377
  - Yi
377
378
  - Unlimited OpenAI-compatible providers
@@ -438,6 +439,10 @@ ROLE_MODIFY_WARNING=true
438
439
  ENABLE_FUNCTIONS=true
439
440
  # Set to false to disable showing function output in the response
440
441
  SHOW_FUNCTION_OUTPUT=true
442
+
443
+ # MCP settings
444
+ ENABLE_MCP=false
445
+ SHOW_MCP_OUTPUT=false
441
446
  ```
442
447
 
443
448
  ### Configuration Options Reference
@@ -468,7 +473,10 @@ SHOW_FUNCTION_OUTPUT=true
468
473
  | `MAX_SAVED_CHATS` | Max saved chats | `20` | `YAI_MAX_SAVED_CHATS` |
469
474
  | `ROLE_MODIFY_WARNING` | Warn user when modifying role | `true` | `YAI_ROLE_MODIFY_WARNING` |
470
475
  | `ENABLE_FUNCTIONS` | Enable function calling | `true` | `YAI_ENABLE_FUNCTIONS` |
471
- | `SHOW_FUNCTION_OUTPUT` | Show function output in response | `true` | `YAI_SHOW_FUNCTION_OUTPUT` |
476
+ | `SHOW_FUNCTION_OUTPUT` | Show function output when calling function | `true` | `YAI_SHOW_FUNCTION_OUTPUT` |
477
+ | `ENABLE_MCP` | Enable MCP tools | `false` | `YAI_ENABLE_MCP` |
478
+ | `SHOW_MCP_OUTPUT` | Show MCP output when calling mcp | `true` | `YAI_SHOW_MCP_OUTPUT` |
479
+
472
480
 
473
481
  ### LLM Provider Configuration
474
482
 
@@ -571,8 +579,10 @@ LOCATION=
571
579
 
572
580
  #### Huggingface
573
581
 
582
+ Default `HF_PROVIDER` is `auto`.
583
+
574
584
  ```ini
575
- HF_PROVIDER=sambanova
585
+ HF_PROVIDER=auto
576
586
  PROVIDER=huggingface
577
587
  API_KEY=
578
588
  MODEL=deepseek-ai/DeepSeek-R1-0528
@@ -1252,6 +1262,83 @@ Thinking:
1252
1262
  Current directory size: 156M (using du -sh .).
1253
1263
  ```
1254
1264
 
1265
+ ### MCP
1266
+
1267
+ Add your MCP config in `~/.config/yaicli/mcp.json` (`C:\Users\<user>\.config\yaicli\mcp.json` on Windows.).
1268
+
1269
+ `--enable-mcp` option is corresponds to the configuration key `ENABLE_MCP`.
1270
+
1271
+ Example:
1272
+
1273
+ ```shell
1274
+ ai 'What is the latest exchange rate between the BTC and the US dollar?' --enable-mcp --show-mcp-output
1275
+
1276
+ Assistant:
1277
+
1278
+ @Mcp call: bing_search({"query": "latest exchange rate between BTC and US dollar"})
1279
+ ╭─ Mcp output ──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
1280
+ │ [ │
1281
+ │ { │
1282
+ │ "id": "result_1751024997243_0", │
1283
+ │ "title": "BTC to USD - Bitcoin to US Dollar Conversion - Exchange Rates", │
1284
+ │ "link": "https://www.exchange-rates.org/converter/btc-usd", │
1285
+ │ "snippet": "11 小时之前 · 1 Bitcoin = 107,304 US Dollars as of June 27, 2025 03:00 AM UTC. You can get live exchange │
1286
+ │ rates between Bitcoin and US Dollars using exchange-rates.org, which aggregates …" │
1287
+ │ }, │
1288
+ │ { │
1289
+ │ "id": "result_1751024997245_1", │
1290
+ │ "title": "Live Bitcoin to US Dollars Exchange Rate - ₿ 1 …", │
1291
+ │ "link": "https://btc.currencyrate.today/usd", │
1292
+ │ "snippet": ".b_imgcap_altitle p strong,.b_imgcap_altitle .b_factrow strong{color:#767676}#b_results │
1293
+ │ .b_imgcap_altitle{line-height:22px}.b_hList img{display:block}..." │
1294
+ │ }, │
1295
+ │ { │
1296
+ │ "id": "result_1751024997246_2", │
1297
+ │ "title": "1 BTC to USD - Bitcoins to US Dollars Exchange Rate - Xe", │
1298
+ │ "link": "https://www.xe.com/currencyconverter/convert/?From=BTC&To=USD", │
1299
+ │ "snippet": "2025年6月15日 · Get the latest 1 Bitcoin to US Dollar rate for FREE with the original Universal Currency │
1300
+ │ Converter. Set rate alerts for to and learn more about Bitcoins and US Dollars from …" │
1301
+ │ }, │
1302
+ │ { │
1303
+ │ "id": "result_1751024997246_3", │
1304
+ │ "title": "BTC to USD Exchange Rates | Best Exchange Rates", │
1305
+ │ "link": "https://bestexchangerates.com/rates/btc-to-usd", │
1306
+ │ "snippet": "Bitcoin (BTC) to US dollar (USD) market data - latest interbank exchange rate, trend, chart & historic │
1307
+ │ rates. Sell BTC → Buy USD" │
1308
+ │ }, │
1309
+ │ { │
1310
+ │ "id": "result_1751024997247_4", │
1311
+ │ "title": "BTC to USD | Bitcoin to US Dollar - Investing.com", │
1312
+ │ "link": "https://www.investing.com/crypto/bitcoin/btc-usd", │
1313
+ │ "snippet": "Bitcoin Eyes 120k as Fed Rate Cuts Hopes Rise, US Dollar Falls to Multi-Year Lows BTC hovers around │
1314
+ │ 107.5k after attempts at 108k Fed rate cut optimism rises USD falls to its lowest level …" │
1315
+ │ } │
1316
+ │ ] │
1317
+ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
1318
+ Here are some current exchange rates for Bitcoin (BTC) to US Dollar (USD):
1319
+
1320
+ 1 Exchange-Rates.org:
1321
+ ₿1 Bitcoin = 💵107,304 US Dollars (as of June 27, 2025, 03:00 AM UTC).
1322
+ Link
1323
+ 2 BTC.CurrencyRate.Today:
1324
+ Live Bitcoin to US Dollars exchange rate.
1325
+ Link
1326
+ 3 Xe.com:
1327
+ Latest conversion rate and information about Bitcoin to US Dollars.
1328
+ Link
1329
+ 4 BestExchangeRates.com:
1330
+ Current BTC to USD market data, including charts and historic rates.
1331
+ Link
1332
+ 5 Investing.com:
1333
+ Bitcoin price analysis and live BTC to USD updates.
1334
+ Link
1335
+
1336
+ For the most accurate and up-to-date rate, I recommend checking one of these sources directly.
1337
+ ```
1338
+
1339
+ ![mcp](artwork/mcp_example.png)
1340
+
1341
+
1255
1342
  ## 💻 Technical Details
1256
1343
 
1257
1344
  ### Architecture
@@ -1,38 +1,37 @@
1
- pyproject.toml,sha256=LQv7NHuPZjn7h03OWDzftK8V0G_OG0626EkpVEUh4IA,2756
1
+ pyproject.toml,sha256=IGnWg-I9UrjiWbtuBK0GwEnTrrsvy6ZCN3GyfAlsGSU,2756
2
2
  yaicli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  yaicli/chat.py,sha256=_emvZEdgMBth2nQGaNWPf0P45oW2k3bpuIwqsxFcM5A,13676
4
4
  yaicli/cli.py,sha256=s8Bj4MSQmLblh4fHBPKS-DvJoAdMmp64KC7e7BwzmOs,23322
5
5
  yaicli/config.py,sha256=_xLHgyW5dDg76bL1SyTcrQCnVs4dFpXafIS3sClshK0,6563
6
6
  yaicli/console.py,sha256=vARPJd-3lafutsQWrGntQVjLrYqaJD3qisN82pmuhjU,1973
7
- yaicli/const.py,sha256=G-EhMsSfOjKQLBhpOAc3pRtjvKmmWrwyyuyuGKHt7wk,8194
8
- yaicli/entry.py,sha256=Q1eqLE7tcHide7ooyPO7OCJpKE2YVuxR-NNFA2Pt2Hw,8693
7
+ yaicli/const.py,sha256=O4hNk3ztjJ1o26lMjM1GK5Jpy-oA9NF69Gy528_ccEM,8861
8
+ yaicli/entry.py,sha256=jz4sVVy2Nr7uFMqpBhodAjtV5zflxpNHhAROKFRAH58,9633
9
9
  yaicli/exceptions.py,sha256=WBYg8OTJJzaj7lt6HE7ZyBoe5T6A3yZRNCRfWd4iN0c,372
10
10
  yaicli/history.py,sha256=s-57X9FMsaQHF7XySq1gGH_jpd_cHHTYafYu2ECuG6M,2472
11
11
  yaicli/printer.py,sha256=g1TS7aDSQlWlSrQRhvNhNqoQKlsaf1lVOyUSK6LQLNQ,7945
12
12
  yaicli/render.py,sha256=k8o2P8fI44PJlyQbs7gmMiu2x2prwajdWn5JIt15BIA,505
13
13
  yaicli/role.py,sha256=PfwiVJIlzg7EzlvMM-kIy6vBK0d5d_J4M1I_fIZGnWk,7399
14
14
  yaicli/schemas.py,sha256=Ty2ybCvld-ritgBZoI3RR93vYfw9LUNqkR8xk8VRZ2A,762
15
- yaicli/tools.py,sha256=xw8KEs_xlSf79A2Aq1rAsUWahS6A_e5QMLt7QDXL5bs,5086
16
- yaicli/utils.py,sha256=bpo3Xhozpxsaci3FtEIKZ32l4ZdyWMsrHjYGX0tB4J4,4541
17
- yaicli/functions/__init__.py,sha256=_FJooQ9GkijG8xLwuU0cr5GBrGnC9Nc6bnCeUjrsT0k,1271
15
+ yaicli/utils.py,sha256=vCc_HoEKakA8HJ2m7_dIiIvMAIKEFlDpv1w1Yial-EE,5552
16
+ yaicli/functions/__init__.py,sha256=o2xwGvLKrvN8Wghhf_bU1klQfFRHW3g-f2BR968g31g,1652
18
17
  yaicli/functions/buildin/execute_shell_command.py,sha256=unl1-F8p6QZajeHdA0u5UpURMJM0WhdWMUWCCCHVRcI,1320
19
18
  yaicli/llms/__init__.py,sha256=x78cJujrJkelXPnzHS6pzHkITZdgLYZqJMnrMHbptoc,134
20
- yaicli/llms/client.py,sha256=mkE9KHSuPcJfpNQXbzF2YXGkel3jrOW8KfQ3YYpaK4M,4453
19
+ yaicli/llms/client.py,sha256=yjv8-DjVORdn1PKOC6t6Ei0uB3dz5GQ2bC3zAdKGALg,5759
21
20
  yaicli/llms/provider.py,sha256=jF15kmY_tZVOjlw0fbHQkEvlmOX57-HBhILzG0KvXyo,3412
22
- yaicli/llms/providers/ai21_provider.py,sha256=SvgGj9_87KEqmxCMLbtsSkT8J3rUD7Mb21UF7pMWsks,3035
23
- yaicli/llms/providers/chatglm_provider.py,sha256=QEzALvY5FBhuDCx6rHjLi7GSRTwTHNUwo8gg4FEdrxs,6466
21
+ yaicli/llms/providers/ai21_provider.py,sha256=1zHG1xYYL4zEA_hNa0YqmplC00evCKuZPU7r2v54Q8o,4551
22
+ yaicli/llms/providers/chatglm_provider.py,sha256=zno_AbFESsRG3E5-IVnqMc1uA-jTuE1u4u5gU0BkAkQ,7421
24
23
  yaicli/llms/providers/chutes_provider.py,sha256=mtvWvRRfHPH3JFfzym87wXtPNiMpLnur3805N9acx7E,882
25
24
  yaicli/llms/providers/cohere_provider.py,sha256=1UPzNqNOwM4_dsP4kvUaL9O6_bKjxm1lO6A0lM7hgS4,10959
26
25
  yaicli/llms/providers/deepseek_provider.py,sha256=iIV97x2ZCcwhGkshc8wpRi-YAnAnmo0n-YRegPlaOwQ,488
27
- yaicli/llms/providers/doubao_provider.py,sha256=4eOdE91ITUn3uo3mvYAzdrHsuFIIBwZWib21mtZn8OY,1938
28
- yaicli/llms/providers/gemini_provider.py,sha256=k_6JFmqiYPz5K8IioFic5tp8KAHgeeakjkPyqJVz8BI,8007
26
+ yaicli/llms/providers/doubao_provider.py,sha256=Tr0EP2fDh9txI-8dp_7BVAMswMkbw0cjBtZd6gI12v8,1453
27
+ yaicli/llms/providers/gemini_provider.py,sha256=78mOQEcf4uWzNduf8-tH0uMyka7xFbHNRnQQvHu_csU,8113
29
28
  yaicli/llms/providers/groq_provider.py,sha256=EiS1Yxw5jbAUBFCRYsJ57KYgZPk6oH-_gD72OfW8Oik,1358
30
- yaicli/llms/providers/huggingface_provider.py,sha256=vDJyyK_aOlvktNvs-cji6pDtmKEp61vuVJ783BZw4pc,1247
29
+ yaicli/llms/providers/huggingface_provider.py,sha256=XigSh4HDx00aYtBivMc2rwRwW6y6Nf0XgwEiFvcil2E,1239
31
30
  yaicli/llms/providers/infiniai_provider.py,sha256=8-nU6QE58PRoZL9b_HzbPp4yi6OGm7rXtfi9z7bJMOg,786
32
31
  yaicli/llms/providers/minimax_provider.py,sha256=W-j3dzrYMEv14bYt2pCPvPUxvxsUs-iMAcGB9yXakFs,744
33
32
  yaicli/llms/providers/modelscope_provider.py,sha256=qWM0T7r0Zf8k3pLzjj7_IFdnmnx7S3rJO0f9rRm8-_A,504
34
33
  yaicli/llms/providers/ollama_provider.py,sha256=pjpYjfnHWnExweZi1KGbT07JGkcxzKPhqICo8dD82D0,6967
35
- yaicli/llms/providers/openai_provider.py,sha256=ENn21QacP2iTcmbxuW7dgiw3_fUr8EGWhNSFR2yxjis,10079
34
+ yaicli/llms/providers/openai_provider.py,sha256=96vqKiVScULb9rfCuzrI7e9wCgXiqjc2Lx9XP8zCf9Q,10546
36
35
  yaicli/llms/providers/openrouter_provider.py,sha256=R-7FrUrCAKPZ3gbnuo0M6rPlVw1mvSBjbLGs_FtZWM0,732
37
36
  yaicli/llms/providers/sambanova_provider.py,sha256=FFLrsvARt1UPAFWWgiuB6zvGzGKdtehKL58HdE1fo_M,2254
38
37
  yaicli/llms/providers/siliconflow_provider.py,sha256=CW2VSt6evUyFy21vN84Nvmw1P0JpmHBLznsgiXMnHM0,496
@@ -40,8 +39,11 @@ yaicli/llms/providers/targon_provider.py,sha256=RQ808eS9lvsyvlzyKaQYcN0NimbpoNWg
40
39
  yaicli/llms/providers/vertexai_provider.py,sha256=_ddrse1LfXRChTgkvxUlexyfJlfr0sVJH-Rmno3djSI,636
41
40
  yaicli/llms/providers/xai_provider.py,sha256=Q6iOvJZOXIAwRiiHMKEBgq8-W6SGVZ9QD1_532bNYfo,199
42
41
  yaicli/llms/providers/yi_provider.py,sha256=EnTm9qTxHPnzERsKqgGnzRIVhXFcAEdYqtOra65pGmY,719
43
- yaicli-0.6.4.dist-info/METADATA,sha256=q1r4B-AADLIC9gAYEDd4BDHnAMnbjqDXrrOG3QNxbGc,55786
44
- yaicli-0.6.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
- yaicli-0.6.4.dist-info/entry_points.txt,sha256=iYVyQP0PJIm9tQnlQheqT435kK_xdGoi5j9aswGV9hA,66
46
- yaicli-0.6.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
47
- yaicli-0.6.4.dist-info/RECORD,,
42
+ yaicli/tools/__init__.py,sha256=62kSqvh232jog_pb85Tsx4Pe_rI9CAODGMTxAXdKzy0,4112
43
+ yaicli/tools/function.py,sha256=1yXnpOg7Y2sw_LwTOBH7042cHeBoBCVJJMQTVyQh_Hw,2802
44
+ yaicli/tools/mcp.py,sha256=-V17cDsQvmdb2eeVnuC_ypg5Rn6f10dUjEptNK1kTxU,15004
45
+ yaicli-0.7.0.dist-info/METADATA,sha256=KnO0NulMNsdWI2Zru1iKvpspzg-8uBZqv5AeXrX1SeU,64329
46
+ yaicli-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
+ yaicli-0.7.0.dist-info/entry_points.txt,sha256=iYVyQP0PJIm9tQnlQheqT435kK_xdGoi5j9aswGV9hA,66
48
+ yaicli-0.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
49
+ yaicli-0.7.0.dist-info/RECORD,,
yaicli/tools.py DELETED
@@ -1,159 +0,0 @@
1
- import importlib.util
2
- import sys
3
- from typing import Any, Dict, List, NewType, Optional, Tuple, cast
4
-
5
- from instructor import OpenAISchema
6
- from json_repair import repair_json
7
- from rich.panel import Panel
8
-
9
- from .config import cfg
10
- from .console import get_console
11
- from .const import FUNCTIONS_DIR
12
- from .schemas import ToolCall
13
-
14
- console = get_console()
15
-
16
- FunctionName = NewType("FunctionName", str)
17
-
18
-
19
- class Function:
20
- """Function description class"""
21
-
22
- def __init__(self, function: type[OpenAISchema]):
23
- self.name = function.openai_schema["name"]
24
- self.description = function.openai_schema.get("description", "")
25
- self.parameters = function.openai_schema.get("parameters", {})
26
- self.execute = function.execute # type: ignore
27
-
28
-
29
- _func_name_map: Optional[dict[FunctionName, Function]] = None
30
-
31
-
32
- def get_func_name_map() -> dict[FunctionName, Function]:
33
- """Get function name map"""
34
- global _func_name_map
35
- if _func_name_map:
36
- return _func_name_map
37
- if not FUNCTIONS_DIR.exists():
38
- FUNCTIONS_DIR.mkdir(parents=True, exist_ok=True)
39
- return {}
40
- functions = []
41
- for file in FUNCTIONS_DIR.glob("*.py"):
42
- if file.name.startswith("_"):
43
- continue
44
- module_name = str(file).replace("/", ".").rstrip(".py")
45
- spec = importlib.util.spec_from_file_location(module_name, str(file))
46
- module = importlib.util.module_from_spec(spec) # type: ignore
47
- sys.modules[module_name] = module
48
- spec.loader.exec_module(module) # type: ignore
49
-
50
- if not issubclass(module.Function, OpenAISchema):
51
- raise TypeError(f"Function {module_name} must be a subclass of instructor.OpenAISchema")
52
- if not hasattr(module.Function, "execute"):
53
- raise TypeError(f"Function {module_name} must have an 'execute' classmethod")
54
-
55
- # Add to function list
56
- functions.append(Function(function=module.Function))
57
-
58
- # Cache the function list
59
- _func_name_map = {FunctionName(func.name): func for func in functions}
60
- return _func_name_map
61
-
62
-
63
- def list_functions() -> list[Function]:
64
- """List all available buildin functions"""
65
- global _func_name_map
66
- if not _func_name_map:
67
- _func_name_map = get_func_name_map()
68
-
69
- return list(_func_name_map.values())
70
-
71
-
72
- def get_function(name: FunctionName) -> Function:
73
- """Get a function by name
74
-
75
- Args:
76
- name: Function name
77
-
78
- Returns:
79
- Function execute method
80
-
81
- Raises:
82
- ValueError: If function not found
83
- """
84
- func_map = get_func_name_map()
85
- if name in func_map:
86
- return func_map[FunctionName(name)]
87
- raise ValueError(f"Function {name!r} not found")
88
-
89
-
90
- def get_openai_schemas() -> List[Dict[str, Any]]:
91
- """Get OpenAI-compatible function schemas
92
-
93
- Returns:
94
- List of function schemas in OpenAI format
95
- """
96
- transformed_schemas = []
97
- for function in list_functions():
98
- schema = {
99
- "type": "function",
100
- "function": {
101
- "name": function.name,
102
- "description": function.description,
103
- "parameters": function.parameters,
104
- },
105
- }
106
- transformed_schemas.append(schema)
107
- return transformed_schemas
108
-
109
-
110
- def execute_tool_call(tool_call: ToolCall) -> Tuple[str, bool]:
111
- """Execute a tool call and return the result
112
-
113
- Args:
114
- tool_call: The tool call to execute
115
-
116
- Returns:
117
- Tuple[str, bool]: (result text, success flag)
118
- """
119
- console.print(f"@Function call: {tool_call.name}({tool_call.arguments})", style="blue")
120
-
121
- # 1. Get the function
122
- try:
123
- function = get_function(FunctionName(tool_call.name))
124
- except ValueError as e:
125
- error_msg = f"Function '{tool_call.name!r}' not exists: {e}"
126
- console.print(error_msg, style="red")
127
- return error_msg, False
128
-
129
- # 2. Parse function arguments
130
- try:
131
- arguments = repair_json(tool_call.arguments, return_objects=True)
132
- if not isinstance(arguments, dict):
133
- error_msg = f"Invalid arguments type: {arguments!r}, should be JSON object"
134
- console.print(error_msg, style="red")
135
- return error_msg, False
136
- arguments = cast(dict, arguments)
137
- except Exception as e:
138
- error_msg = f"Invalid arguments from llm: {e}\nRaw arguments: {tool_call.arguments!r}"
139
- console.print(error_msg, style="red")
140
- return error_msg, False
141
-
142
- # 3. Execute the function
143
- try:
144
- function_result = function.execute(**arguments)
145
- if cfg["SHOW_FUNCTION_OUTPUT"]:
146
- panel = Panel(
147
- function_result,
148
- title="Function output",
149
- title_align="left",
150
- expand=False,
151
- border_style="blue",
152
- style="dim",
153
- )
154
- console.print(panel)
155
- return function_result, True
156
- except Exception as e:
157
- error_msg = f"Call function error: {e}\nFunction name: {tool_call.name!r}\nArguments: {arguments!r}"
158
- console.print(error_msg, style="red")
159
- return error_msg, False
File without changes