yaicli 0.6.4__py3-none-any.whl → 0.7.1__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.
- pyproject.toml +5 -1
- yaicli/const.py +17 -6
- yaicli/entry.py +24 -1
- yaicli/functions/__init__.py +13 -1
- yaicli/llms/client.py +71 -38
- yaicli/llms/providers/ai21_provider.py +33 -1
- yaicli/llms/providers/chatglm_provider.py +30 -7
- yaicli/llms/providers/doubao_provider.py +0 -14
- yaicli/llms/providers/gemini_provider.py +21 -22
- yaicli/llms/providers/huggingface_provider.py +1 -1
- yaicli/llms/providers/openai_provider.py +17 -8
- yaicli/tools/__init__.py +127 -0
- yaicli/tools/function.py +90 -0
- yaicli/tools/mcp.py +459 -0
- yaicli/utils.py +34 -0
- {yaicli-0.6.4.dist-info → yaicli-0.7.1.dist-info}/METADATA +94 -4
- {yaicli-0.6.4.dist-info → yaicli-0.7.1.dist-info}/RECORD +20 -18
- yaicli/tools.py +0 -159
- {yaicli-0.6.4.dist-info → yaicli-0.7.1.dist-info}/WHEEL +0 -0
- {yaicli-0.6.4.dist-info → yaicli-0.7.1.dist-info}/entry_points.txt +0 -0
- {yaicli-0.6.4.dist-info → yaicli-0.7.1.dist-info}/licenses/LICENSE +0 -0
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.
|
3
|
+
Version: 0.7.1
|
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
|
@@ -215,6 +215,7 @@ Classifier: Programming Language :: Python :: 3
|
|
215
215
|
Requires-Python: >=3.10
|
216
216
|
Requires-Dist: click>=8.1.8
|
217
217
|
Requires-Dist: distro>=1.9.0
|
218
|
+
Requires-Dist: fastmcp>=2.9.2
|
218
219
|
Requires-Dist: httpx>=0.28.1
|
219
220
|
Requires-Dist: instructor>=1.7.9
|
220
221
|
Requires-Dist: json-repair>=0.44.1
|
@@ -237,6 +238,8 @@ Provides-Extra: gemini
|
|
237
238
|
Requires-Dist: google-genai>=1.20.0; extra == 'gemini'
|
238
239
|
Provides-Extra: huggingface
|
239
240
|
Requires-Dist: huggingface-hub>=0.33.0; extra == 'huggingface'
|
241
|
+
Provides-Extra: mistral
|
242
|
+
Requires-Dist: mistralai>=1.8.2; extra == 'mistral'
|
240
243
|
Provides-Extra: ollama
|
241
244
|
Requires-Dist: ollama>=0.5.1; extra == 'ollama'
|
242
245
|
Description-Content-Type: text/markdown
|
@@ -265,6 +268,7 @@ generate and execute shell commands, or get quick answers without leaving your w
|
|
265
268
|
> [!NOTE]
|
266
269
|
> YAICLI is actively developed. While core functionality is stable, some features may evolve in future releases.
|
267
270
|
|
271
|
+
> We support MCP since v0.7.0!
|
268
272
|
> We support Function Call since v0.5.0!
|
269
273
|
|
270
274
|
## ✨ Key Features
|
@@ -362,7 +366,6 @@ pip install .
|
|
362
366
|
- Deepseek
|
363
367
|
- Doubao
|
364
368
|
- Gemini
|
365
|
-
- Vertex AI
|
366
369
|
- Groq
|
367
370
|
- Huggingface
|
368
371
|
- Minimax
|
@@ -372,6 +375,7 @@ pip install .
|
|
372
375
|
- Sambanova
|
373
376
|
- Siliconflow
|
374
377
|
- Targon
|
378
|
+
- Vertex ai
|
375
379
|
- X AI
|
376
380
|
- Yi
|
377
381
|
- Unlimited OpenAI-compatible providers
|
@@ -438,6 +442,10 @@ ROLE_MODIFY_WARNING=true
|
|
438
442
|
ENABLE_FUNCTIONS=true
|
439
443
|
# Set to false to disable showing function output in the response
|
440
444
|
SHOW_FUNCTION_OUTPUT=true
|
445
|
+
|
446
|
+
# MCP settings
|
447
|
+
ENABLE_MCP=false
|
448
|
+
SHOW_MCP_OUTPUT=false
|
441
449
|
```
|
442
450
|
|
443
451
|
### Configuration Options Reference
|
@@ -468,7 +476,10 @@ SHOW_FUNCTION_OUTPUT=true
|
|
468
476
|
| `MAX_SAVED_CHATS` | Max saved chats | `20` | `YAI_MAX_SAVED_CHATS` |
|
469
477
|
| `ROLE_MODIFY_WARNING` | Warn user when modifying role | `true` | `YAI_ROLE_MODIFY_WARNING` |
|
470
478
|
| `ENABLE_FUNCTIONS` | Enable function calling | `true` | `YAI_ENABLE_FUNCTIONS` |
|
471
|
-
| `SHOW_FUNCTION_OUTPUT` | Show function output
|
479
|
+
| `SHOW_FUNCTION_OUTPUT` | Show function output when calling function | `true` | `YAI_SHOW_FUNCTION_OUTPUT` |
|
480
|
+
| `ENABLE_MCP` | Enable MCP tools | `false` | `YAI_ENABLE_MCP` |
|
481
|
+
| `SHOW_MCP_OUTPUT` | Show MCP output when calling mcp | `true` | `YAI_SHOW_MCP_OUTPUT` |
|
482
|
+
|
472
483
|
|
473
484
|
### LLM Provider Configuration
|
474
485
|
|
@@ -571,8 +582,10 @@ LOCATION=
|
|
571
582
|
|
572
583
|
#### Huggingface
|
573
584
|
|
585
|
+
Default `HF_PROVIDER` is `auto`.
|
586
|
+
|
574
587
|
```ini
|
575
|
-
HF_PROVIDER=
|
588
|
+
HF_PROVIDER=auto
|
576
589
|
PROVIDER=huggingface
|
577
590
|
API_KEY=
|
578
591
|
MODEL=deepseek-ai/DeepSeek-R1-0528
|
@@ -1252,6 +1265,83 @@ Thinking:
|
|
1252
1265
|
Current directory size: 156M (using du -sh .).
|
1253
1266
|
```
|
1254
1267
|
|
1268
|
+
### MCP
|
1269
|
+
|
1270
|
+
Add your MCP config in `~/.config/yaicli/mcp.json` (`C:\Users\<user>\.config\yaicli\mcp.json` on Windows.).
|
1271
|
+
|
1272
|
+
`--enable-mcp` option is corresponds to the configuration key `ENABLE_MCP`.
|
1273
|
+
|
1274
|
+
Example:
|
1275
|
+
|
1276
|
+
```shell
|
1277
|
+
ai 'What is the latest exchange rate between the BTC and the US dollar?' --enable-mcp --show-mcp-output
|
1278
|
+
|
1279
|
+
Assistant:
|
1280
|
+
|
1281
|
+
@Mcp call: bing_search({"query": "latest exchange rate between BTC and US dollar"})
|
1282
|
+
╭─ Mcp output ──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
1283
|
+
│ [ │
|
1284
|
+
│ { │
|
1285
|
+
│ "id": "result_1751024997243_0", │
|
1286
|
+
│ "title": "BTC to USD - Bitcoin to US Dollar Conversion - Exchange Rates", │
|
1287
|
+
│ "link": "https://www.exchange-rates.org/converter/btc-usd", │
|
1288
|
+
│ "snippet": "11 小时之前 · 1 Bitcoin = 107,304 US Dollars as of June 27, 2025 03:00 AM UTC. You can get live exchange │
|
1289
|
+
│ rates between Bitcoin and US Dollars using exchange-rates.org, which aggregates …" │
|
1290
|
+
│ }, │
|
1291
|
+
│ { │
|
1292
|
+
│ "id": "result_1751024997245_1", │
|
1293
|
+
│ "title": "Live Bitcoin to US Dollars Exchange Rate - ₿ 1 …", │
|
1294
|
+
│ "link": "https://btc.currencyrate.today/usd", │
|
1295
|
+
│ "snippet": ".b_imgcap_altitle p strong,.b_imgcap_altitle .b_factrow strong{color:#767676}#b_results │
|
1296
|
+
│ .b_imgcap_altitle{line-height:22px}.b_hList img{display:block}..." │
|
1297
|
+
│ }, │
|
1298
|
+
│ { │
|
1299
|
+
│ "id": "result_1751024997246_2", │
|
1300
|
+
│ "title": "1 BTC to USD - Bitcoins to US Dollars Exchange Rate - Xe", │
|
1301
|
+
│ "link": "https://www.xe.com/currencyconverter/convert/?From=BTC&To=USD", │
|
1302
|
+
│ "snippet": "2025年6月15日 · Get the latest 1 Bitcoin to US Dollar rate for FREE with the original Universal Currency │
|
1303
|
+
│ Converter. Set rate alerts for to and learn more about Bitcoins and US Dollars from …" │
|
1304
|
+
│ }, │
|
1305
|
+
│ { │
|
1306
|
+
│ "id": "result_1751024997246_3", │
|
1307
|
+
│ "title": "BTC to USD Exchange Rates | Best Exchange Rates", │
|
1308
|
+
│ "link": "https://bestexchangerates.com/rates/btc-to-usd", │
|
1309
|
+
│ "snippet": "Bitcoin (BTC) to US dollar (USD) market data - latest interbank exchange rate, trend, chart & historic │
|
1310
|
+
│ rates. Sell BTC → Buy USD" │
|
1311
|
+
│ }, │
|
1312
|
+
│ { │
|
1313
|
+
│ "id": "result_1751024997247_4", │
|
1314
|
+
│ "title": "BTC to USD | Bitcoin to US Dollar - Investing.com", │
|
1315
|
+
│ "link": "https://www.investing.com/crypto/bitcoin/btc-usd", │
|
1316
|
+
│ "snippet": "Bitcoin Eyes 120k as Fed Rate Cuts Hopes Rise, US Dollar Falls to Multi-Year Lows BTC hovers around │
|
1317
|
+
│ 107.5k after attempts at 108k Fed rate cut optimism rises USD falls to its lowest level …" │
|
1318
|
+
│ } │
|
1319
|
+
│ ] │
|
1320
|
+
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
1321
|
+
Here are some current exchange rates for Bitcoin (BTC) to US Dollar (USD):
|
1322
|
+
|
1323
|
+
1 Exchange-Rates.org:
|
1324
|
+
₿1 Bitcoin = 💵107,304 US Dollars (as of June 27, 2025, 03:00 AM UTC).
|
1325
|
+
Link
|
1326
|
+
2 BTC.CurrencyRate.Today:
|
1327
|
+
Live Bitcoin to US Dollars exchange rate.
|
1328
|
+
Link
|
1329
|
+
3 Xe.com:
|
1330
|
+
Latest conversion rate and information about Bitcoin to US Dollars.
|
1331
|
+
Link
|
1332
|
+
4 BestExchangeRates.com:
|
1333
|
+
Current BTC to USD market data, including charts and historic rates.
|
1334
|
+
Link
|
1335
|
+
5 Investing.com:
|
1336
|
+
Bitcoin price analysis and live BTC to USD updates.
|
1337
|
+
Link
|
1338
|
+
|
1339
|
+
For the most accurate and up-to-date rate, I recommend checking one of these sources directly.
|
1340
|
+
```
|
1341
|
+
|
1342
|
+

|
1343
|
+
|
1344
|
+
|
1255
1345
|
## 💻 Technical Details
|
1256
1346
|
|
1257
1347
|
### Architecture
|
@@ -1,38 +1,37 @@
|
|
1
|
-
pyproject.toml,sha256=
|
1
|
+
pyproject.toml,sha256=nnGhGl-8nx5sHrGTF6TL9nHNZwUKFIAuBBTVSauePZ0,2816
|
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=
|
8
|
-
yaicli/entry.py,sha256=
|
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/
|
16
|
-
yaicli/
|
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=
|
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=
|
23
|
-
yaicli/llms/providers/chatglm_provider.py,sha256=
|
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=
|
28
|
-
yaicli/llms/providers/gemini_provider.py,sha256=
|
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=
|
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=
|
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
|
44
|
-
yaicli
|
45
|
-
yaicli
|
46
|
-
yaicli-0.
|
47
|
-
yaicli-0.
|
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.1.dist-info/METADATA,sha256=8JNcElJu4dgwlbp7G11Ik0H20K6zrX7cHzdI7nZwbNA,64435
|
46
|
+
yaicli-0.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
47
|
+
yaicli-0.7.1.dist-info/entry_points.txt,sha256=iYVyQP0PJIm9tQnlQheqT435kK_xdGoi5j9aswGV9hA,66
|
48
|
+
yaicli-0.7.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
49
|
+
yaicli-0.7.1.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
|
File without changes
|
File without changes
|