yaicli 0.6.4__tar.gz → 0.7.0__tar.gz
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-0.6.4 → yaicli-0.7.0}/PKG-INFO +91 -4
- {yaicli-0.6.4 → yaicli-0.7.0}/README.md +90 -3
- {yaicli-0.6.4 → yaicli-0.7.0}/pyproject.toml +1 -1
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/const.py +17 -6
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/entry.py +24 -1
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/functions/__init__.py +13 -1
- yaicli-0.7.0/yaicli/llms/client.py +153 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/ai21_provider.py +33 -1
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/chatglm_provider.py +30 -7
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/doubao_provider.py +0 -14
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/gemini_provider.py +21 -22
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/huggingface_provider.py +1 -1
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/openai_provider.py +17 -8
- yaicli-0.7.0/yaicli/tools/__init__.py +127 -0
- yaicli-0.7.0/yaicli/tools/function.py +90 -0
- yaicli-0.7.0/yaicli/tools/mcp.py +459 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/utils.py +34 -0
- yaicli-0.6.4/yaicli/llms/client.py +0 -120
- yaicli-0.6.4/yaicli/tools.py +0 -159
- {yaicli-0.6.4 → yaicli-0.7.0}/.gitignore +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/LICENSE +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/__init__.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/chat.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/cli.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/config.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/console.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/exceptions.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/functions/buildin/execute_shell_command.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/history.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/__init__.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/chutes_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/cohere_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/deepseek_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/groq_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/infiniai_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/minimax_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/modelscope_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/ollama_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/openrouter_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/sambanova_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/siliconflow_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/targon_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/vertexai_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/xai_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/llms/providers/yi_provider.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/printer.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/render.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/role.py +0 -0
- {yaicli-0.6.4 → yaicli-0.7.0}/yaicli/schemas.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: yaicli
|
3
|
-
Version: 0.
|
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
|
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=
|
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
|
+

|
1340
|
+
|
1341
|
+
|
1255
1342
|
## 💻 Technical Details
|
1256
1343
|
|
1257
1344
|
### Architecture
|
@@ -22,6 +22,7 @@ generate and execute shell commands, or get quick answers without leaving your w
|
|
22
22
|
> [!NOTE]
|
23
23
|
> YAICLI is actively developed. While core functionality is stable, some features may evolve in future releases.
|
24
24
|
|
25
|
+
> We support MCP since v0.7.0!
|
25
26
|
> We support Function Call since v0.5.0!
|
26
27
|
|
27
28
|
## ✨ Key Features
|
@@ -119,7 +120,6 @@ pip install .
|
|
119
120
|
- Deepseek
|
120
121
|
- Doubao
|
121
122
|
- Gemini
|
122
|
-
- Vertex AI
|
123
123
|
- Groq
|
124
124
|
- Huggingface
|
125
125
|
- Minimax
|
@@ -129,6 +129,7 @@ pip install .
|
|
129
129
|
- Sambanova
|
130
130
|
- Siliconflow
|
131
131
|
- Targon
|
132
|
+
- Vertex ai
|
132
133
|
- X AI
|
133
134
|
- Yi
|
134
135
|
- Unlimited OpenAI-compatible providers
|
@@ -195,6 +196,10 @@ ROLE_MODIFY_WARNING=true
|
|
195
196
|
ENABLE_FUNCTIONS=true
|
196
197
|
# Set to false to disable showing function output in the response
|
197
198
|
SHOW_FUNCTION_OUTPUT=true
|
199
|
+
|
200
|
+
# MCP settings
|
201
|
+
ENABLE_MCP=false
|
202
|
+
SHOW_MCP_OUTPUT=false
|
198
203
|
```
|
199
204
|
|
200
205
|
### Configuration Options Reference
|
@@ -225,7 +230,10 @@ SHOW_FUNCTION_OUTPUT=true
|
|
225
230
|
| `MAX_SAVED_CHATS` | Max saved chats | `20` | `YAI_MAX_SAVED_CHATS` |
|
226
231
|
| `ROLE_MODIFY_WARNING` | Warn user when modifying role | `true` | `YAI_ROLE_MODIFY_WARNING` |
|
227
232
|
| `ENABLE_FUNCTIONS` | Enable function calling | `true` | `YAI_ENABLE_FUNCTIONS` |
|
228
|
-
| `SHOW_FUNCTION_OUTPUT` | Show function output
|
233
|
+
| `SHOW_FUNCTION_OUTPUT` | Show function output when calling function | `true` | `YAI_SHOW_FUNCTION_OUTPUT` |
|
234
|
+
| `ENABLE_MCP` | Enable MCP tools | `false` | `YAI_ENABLE_MCP` |
|
235
|
+
| `SHOW_MCP_OUTPUT` | Show MCP output when calling mcp | `true` | `YAI_SHOW_MCP_OUTPUT` |
|
236
|
+
|
229
237
|
|
230
238
|
### LLM Provider Configuration
|
231
239
|
|
@@ -328,8 +336,10 @@ LOCATION=
|
|
328
336
|
|
329
337
|
#### Huggingface
|
330
338
|
|
339
|
+
Default `HF_PROVIDER` is `auto`.
|
340
|
+
|
331
341
|
```ini
|
332
|
-
HF_PROVIDER=
|
342
|
+
HF_PROVIDER=auto
|
333
343
|
PROVIDER=huggingface
|
334
344
|
API_KEY=
|
335
345
|
MODEL=deepseek-ai/DeepSeek-R1-0528
|
@@ -1009,6 +1019,83 @@ Thinking:
|
|
1009
1019
|
Current directory size: 156M (using du -sh .).
|
1010
1020
|
```
|
1011
1021
|
|
1022
|
+
### MCP
|
1023
|
+
|
1024
|
+
Add your MCP config in `~/.config/yaicli/mcp.json` (`C:\Users\<user>\.config\yaicli\mcp.json` on Windows.).
|
1025
|
+
|
1026
|
+
`--enable-mcp` option is corresponds to the configuration key `ENABLE_MCP`.
|
1027
|
+
|
1028
|
+
Example:
|
1029
|
+
|
1030
|
+
```shell
|
1031
|
+
ai 'What is the latest exchange rate between the BTC and the US dollar?' --enable-mcp --show-mcp-output
|
1032
|
+
|
1033
|
+
Assistant:
|
1034
|
+
|
1035
|
+
@Mcp call: bing_search({"query": "latest exchange rate between BTC and US dollar"})
|
1036
|
+
╭─ Mcp output ──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
1037
|
+
│ [ │
|
1038
|
+
│ { │
|
1039
|
+
│ "id": "result_1751024997243_0", │
|
1040
|
+
│ "title": "BTC to USD - Bitcoin to US Dollar Conversion - Exchange Rates", │
|
1041
|
+
│ "link": "https://www.exchange-rates.org/converter/btc-usd", │
|
1042
|
+
│ "snippet": "11 小时之前 · 1 Bitcoin = 107,304 US Dollars as of June 27, 2025 03:00 AM UTC. You can get live exchange │
|
1043
|
+
│ rates between Bitcoin and US Dollars using exchange-rates.org, which aggregates …" │
|
1044
|
+
│ }, │
|
1045
|
+
│ { │
|
1046
|
+
│ "id": "result_1751024997245_1", │
|
1047
|
+
│ "title": "Live Bitcoin to US Dollars Exchange Rate - ₿ 1 …", │
|
1048
|
+
│ "link": "https://btc.currencyrate.today/usd", │
|
1049
|
+
│ "snippet": ".b_imgcap_altitle p strong,.b_imgcap_altitle .b_factrow strong{color:#767676}#b_results │
|
1050
|
+
│ .b_imgcap_altitle{line-height:22px}.b_hList img{display:block}..." │
|
1051
|
+
│ }, │
|
1052
|
+
│ { │
|
1053
|
+
│ "id": "result_1751024997246_2", │
|
1054
|
+
│ "title": "1 BTC to USD - Bitcoins to US Dollars Exchange Rate - Xe", │
|
1055
|
+
│ "link": "https://www.xe.com/currencyconverter/convert/?From=BTC&To=USD", │
|
1056
|
+
│ "snippet": "2025年6月15日 · Get the latest 1 Bitcoin to US Dollar rate for FREE with the original Universal Currency │
|
1057
|
+
│ Converter. Set rate alerts for to and learn more about Bitcoins and US Dollars from …" │
|
1058
|
+
│ }, │
|
1059
|
+
│ { │
|
1060
|
+
│ "id": "result_1751024997246_3", │
|
1061
|
+
│ "title": "BTC to USD Exchange Rates | Best Exchange Rates", │
|
1062
|
+
│ "link": "https://bestexchangerates.com/rates/btc-to-usd", │
|
1063
|
+
│ "snippet": "Bitcoin (BTC) to US dollar (USD) market data - latest interbank exchange rate, trend, chart & historic │
|
1064
|
+
│ rates. Sell BTC → Buy USD" │
|
1065
|
+
│ }, │
|
1066
|
+
│ { │
|
1067
|
+
│ "id": "result_1751024997247_4", │
|
1068
|
+
│ "title": "BTC to USD | Bitcoin to US Dollar - Investing.com", │
|
1069
|
+
│ "link": "https://www.investing.com/crypto/bitcoin/btc-usd", │
|
1070
|
+
│ "snippet": "Bitcoin Eyes 120k as Fed Rate Cuts Hopes Rise, US Dollar Falls to Multi-Year Lows BTC hovers around │
|
1071
|
+
│ 107.5k after attempts at 108k Fed rate cut optimism rises USD falls to its lowest level …" │
|
1072
|
+
│ } │
|
1073
|
+
│ ] │
|
1074
|
+
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
1075
|
+
Here are some current exchange rates for Bitcoin (BTC) to US Dollar (USD):
|
1076
|
+
|
1077
|
+
1 Exchange-Rates.org:
|
1078
|
+
₿1 Bitcoin = 💵107,304 US Dollars (as of June 27, 2025, 03:00 AM UTC).
|
1079
|
+
Link
|
1080
|
+
2 BTC.CurrencyRate.Today:
|
1081
|
+
Live Bitcoin to US Dollars exchange rate.
|
1082
|
+
Link
|
1083
|
+
3 Xe.com:
|
1084
|
+
Latest conversion rate and information about Bitcoin to US Dollars.
|
1085
|
+
Link
|
1086
|
+
4 BestExchangeRates.com:
|
1087
|
+
Current BTC to USD market data, including charts and historic rates.
|
1088
|
+
Link
|
1089
|
+
5 Investing.com:
|
1090
|
+
Bitcoin price analysis and live BTC to USD updates.
|
1091
|
+
Link
|
1092
|
+
|
1093
|
+
For the most accurate and up-to-date rate, I recommend checking one of these sources directly.
|
1094
|
+
```
|
1095
|
+
|
1096
|
+

|
1097
|
+
|
1098
|
+
|
1012
1099
|
## 💻 Technical Details
|
1013
1100
|
|
1014
1101
|
### Architecture
|
@@ -16,7 +16,7 @@ from rich.console import JustifyMethod
|
|
16
16
|
BOOL_STR = Literal["true", "false", "yes", "no", "y", "n", "1", "0", "on", "off"]
|
17
17
|
|
18
18
|
|
19
|
-
class JustifyEnum(StrEnum):
|
19
|
+
class JustifyEnum(StrEnum): # type: ignore
|
20
20
|
DEFAULT = "default"
|
21
21
|
LEFT = "left"
|
22
22
|
CENTER = "center"
|
@@ -43,6 +43,7 @@ HISTORY_FILE = Path("~/.yaicli_history").expanduser()
|
|
43
43
|
CONFIG_PATH = Path("~/.config/yaicli/config.ini").expanduser()
|
44
44
|
ROLES_DIR = CONFIG_PATH.parent / "roles"
|
45
45
|
FUNCTIONS_DIR = CONFIG_PATH.parent / "functions"
|
46
|
+
MCP_JSON_PATH = CONFIG_PATH.parent / "mcp.json"
|
46
47
|
|
47
48
|
# Default configuration values
|
48
49
|
DEFAULT_CODE_THEME = "monokai"
|
@@ -69,6 +70,8 @@ DEFAULT_ROLE_MODIFY_WARNING: BOOL_STR = "true"
|
|
69
70
|
DEFAULT_ENABLE_FUNCTIONS: BOOL_STR = "true"
|
70
71
|
DEFAULT_SHOW_FUNCTION_OUTPUT: BOOL_STR = "true"
|
71
72
|
DEFAULT_REASONING_EFFORT: Optional[Literal["low", "high", "medium"]] = None
|
73
|
+
DEFAULT_ENABLE_MCP: BOOL_STR = "false"
|
74
|
+
DEFAULT_SHOW_MCP_OUTPUT: BOOL_STR = "false"
|
72
75
|
|
73
76
|
|
74
77
|
SHELL_PROMPT = """You are YAICLI, a shell command generator.
|
@@ -93,16 +96,16 @@ CODER_PROMPT = (
|
|
93
96
|
)
|
94
97
|
|
95
98
|
|
96
|
-
class DefaultRoleNames(StrEnum):
|
99
|
+
class DefaultRoleNames(StrEnum): # type: ignore
|
97
100
|
SHELL = "Shell Command Generator"
|
98
101
|
DEFAULT = "DEFAULT"
|
99
102
|
CODER = "Code Assistant"
|
100
103
|
|
101
104
|
|
102
105
|
DEFAULT_ROLES: dict[str, dict[str, Any]] = {
|
103
|
-
DefaultRoleNames.SHELL.value: {"name": DefaultRoleNames.SHELL.value, "prompt": SHELL_PROMPT},
|
104
|
-
DefaultRoleNames.DEFAULT.value: {"name": DefaultRoleNames.DEFAULT.value, "prompt": DEFAULT_PROMPT},
|
105
|
-
DefaultRoleNames.CODER.value: {"name": DefaultRoleNames.CODER.value, "prompt": CODER_PROMPT},
|
106
|
+
DefaultRoleNames.SHELL.value: {"name": DefaultRoleNames.SHELL.value, "prompt": SHELL_PROMPT}, # type: ignore
|
107
|
+
DefaultRoleNames.DEFAULT.value: {"name": DefaultRoleNames.DEFAULT.value, "prompt": DEFAULT_PROMPT}, # type: ignore
|
108
|
+
DefaultRoleNames.CODER.value: {"name": DefaultRoleNames.CODER.value, "prompt": CODER_PROMPT}, # type: ignore
|
106
109
|
}
|
107
110
|
|
108
111
|
# DEFAULT_CONFIG_MAP is a dictionary of the configuration options.
|
@@ -151,6 +154,8 @@ DEFAULT_CONFIG_MAP = {
|
|
151
154
|
"env_key": "YAI_SHOW_FUNCTION_OUTPUT",
|
152
155
|
"type": bool,
|
153
156
|
},
|
157
|
+
"ENABLE_MCP": {"value": DEFAULT_ENABLE_MCP, "env_key": "YAI_ENABLE_MCP", "type": bool},
|
158
|
+
"SHOW_MCP_OUTPUT": {"value": DEFAULT_SHOW_MCP_OUTPUT, "env_key": "YAI_SHOW_MCP_OUTPUT", "type": bool},
|
154
159
|
}
|
155
160
|
|
156
161
|
DEFAULT_CONFIG_INI = f"""[core]
|
@@ -201,6 +206,12 @@ ROLE_MODIFY_WARNING={DEFAULT_CONFIG_MAP["ROLE_MODIFY_WARNING"]["value"]}
|
|
201
206
|
# Function settings
|
202
207
|
# Set to false to disable sending functions in API requests
|
203
208
|
ENABLE_FUNCTIONS={DEFAULT_CONFIG_MAP["ENABLE_FUNCTIONS"]["value"]}
|
204
|
-
# Set to false to disable showing function output
|
209
|
+
# Set to false to disable showing function output when calling functions
|
205
210
|
SHOW_FUNCTION_OUTPUT={DEFAULT_CONFIG_MAP["SHOW_FUNCTION_OUTPUT"]["value"]}
|
211
|
+
|
212
|
+
# MCP settings
|
213
|
+
# Set to false to disable MCP in API requests
|
214
|
+
ENABLE_MCP={DEFAULT_CONFIG_MAP["ENABLE_MCP"]["value"]}
|
215
|
+
# Set to false to disable showing MCP output when calling MCP tools
|
216
|
+
SHOW_MCP_OUTPUT={DEFAULT_CONFIG_MAP["SHOW_MCP_OUTPUT"]["value"]}
|
206
217
|
"""
|
@@ -6,7 +6,7 @@ import typer
|
|
6
6
|
from .chat import FileChatManager
|
7
7
|
from .config import cfg
|
8
8
|
from .const import DEFAULT_CONFIG_INI, DefaultRoleNames, JustifyEnum
|
9
|
-
from .functions import install_functions, print_functions
|
9
|
+
from .functions import install_functions, print_functions, print_mcp
|
10
10
|
from .role import RoleManager
|
11
11
|
|
12
12
|
app = typer.Typer(
|
@@ -209,6 +209,29 @@ def main(
|
|
209
209
|
show_default=False,
|
210
210
|
callback=override_config,
|
211
211
|
),
|
212
|
+
# ------------------- MCP Options -------------------
|
213
|
+
enable_mcp: bool = typer.Option( # noqa: F841
|
214
|
+
cfg["ENABLE_MCP"],
|
215
|
+
"--enable-mcp/--disable-mcp",
|
216
|
+
help=f"Enable/disable MCP in API requests [dim](default: {'enabled' if cfg['ENABLE_MCP'] else 'disabled'})[/dim]",
|
217
|
+
rich_help_panel="MCP Options",
|
218
|
+
callback=override_config,
|
219
|
+
),
|
220
|
+
show_mcp_output: bool = typer.Option( # noqa: F841
|
221
|
+
cfg["SHOW_MCP_OUTPUT"],
|
222
|
+
"--show-mcp-output/--hide-mcp-output",
|
223
|
+
help=f"Show the output of MCP [dim](default: {'show' if cfg['SHOW_MCP_OUTPUT'] else 'hide'})[/dim]",
|
224
|
+
rich_help_panel="MCP Options",
|
225
|
+
show_default=False,
|
226
|
+
callback=override_config,
|
227
|
+
),
|
228
|
+
list_mcp: bool = typer.Option( # noqa: F841
|
229
|
+
False,
|
230
|
+
"--list-mcp",
|
231
|
+
help="List all available mcp.",
|
232
|
+
rich_help_panel="MCP Options",
|
233
|
+
callback=print_mcp,
|
234
|
+
),
|
212
235
|
):
|
213
236
|
"""YAICLI: Your AI assistant in the command line.
|
214
237
|
|
@@ -1,9 +1,10 @@
|
|
1
|
+
import json
|
1
2
|
import shutil
|
2
3
|
from pathlib import Path
|
3
4
|
from typing import Any
|
4
5
|
|
5
6
|
from ..console import get_console
|
6
|
-
from ..const import FUNCTIONS_DIR
|
7
|
+
from ..const import FUNCTIONS_DIR, MCP_JSON_PATH
|
7
8
|
from ..utils import option_callback
|
8
9
|
|
9
10
|
console = get_console()
|
@@ -37,3 +38,14 @@ def print_functions(cls, _: Any) -> None:
|
|
37
38
|
if file.name.startswith("_"):
|
38
39
|
continue
|
39
40
|
console.print(file)
|
41
|
+
|
42
|
+
|
43
|
+
@option_callback
|
44
|
+
def print_mcp(cls, _: Any) -> None:
|
45
|
+
"""List all available mcp"""
|
46
|
+
if not MCP_JSON_PATH.exists():
|
47
|
+
console.print("No mcp config found, please add your mcp config in ~/.config/yaicli/mcp.json")
|
48
|
+
return
|
49
|
+
with open(MCP_JSON_PATH, "r") as f:
|
50
|
+
mcp_config = json.load(f)
|
51
|
+
console.print_json(data=mcp_config)
|
@@ -0,0 +1,153 @@
|
|
1
|
+
from typing import Generator, List, Optional, Union
|
2
|
+
|
3
|
+
from ..config import cfg
|
4
|
+
from ..console import get_console
|
5
|
+
from ..schemas import ChatMessage, LLMResponse, RefreshLive, ToolCall
|
6
|
+
from ..tools import execute_tool_call
|
7
|
+
from ..tools.mcp import MCP_TOOL_NAME_PREFIX
|
8
|
+
from .provider import Provider, ProviderFactory
|
9
|
+
|
10
|
+
|
11
|
+
class LLMClient:
|
12
|
+
"""
|
13
|
+
LLM Client that coordinates provider interactions and tool calling
|
14
|
+
|
15
|
+
This class handles the higher level logic of:
|
16
|
+
1. Getting responses from LLM providers
|
17
|
+
2. Managing tool calls and their execution
|
18
|
+
3. Handling conversation flow with tools
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
provider: Optional[Provider] = None,
|
24
|
+
provider_name: str = "",
|
25
|
+
config: dict = cfg,
|
26
|
+
verbose: bool = False,
|
27
|
+
**kwargs,
|
28
|
+
):
|
29
|
+
"""
|
30
|
+
Initialize LLM client
|
31
|
+
|
32
|
+
Args:
|
33
|
+
provider: Optional pre-initialized Provider instance
|
34
|
+
provider_name: Name of the provider to use if provider not provided
|
35
|
+
config: Configuration dictionary
|
36
|
+
verbose: Whether to enable verbose logging
|
37
|
+
"""
|
38
|
+
self.config = config
|
39
|
+
self.verbose = verbose
|
40
|
+
self.console = get_console()
|
41
|
+
self.enable_function = self.config["ENABLE_FUNCTIONS"]
|
42
|
+
self.enable_mcp = self.config["ENABLE_MCP"]
|
43
|
+
|
44
|
+
# Use provided provider or create one
|
45
|
+
if provider:
|
46
|
+
self.provider = provider
|
47
|
+
elif provider_name:
|
48
|
+
self.provider = ProviderFactory.create_provider(provider_name, config=config, verbose=verbose, **kwargs)
|
49
|
+
else:
|
50
|
+
provider_name = config.get("PROVIDER", "openai").lower()
|
51
|
+
self.provider = ProviderFactory.create_provider(provider_name, config=config, verbose=verbose, **kwargs)
|
52
|
+
|
53
|
+
self.max_recursion_depth = config.get("MAX_RECURSION_DEPTH", 5)
|
54
|
+
|
55
|
+
def completion_with_tools(
|
56
|
+
self,
|
57
|
+
messages: List[ChatMessage],
|
58
|
+
stream: bool = False,
|
59
|
+
recursion_depth: int = 0,
|
60
|
+
) -> Generator[Union[LLMResponse, RefreshLive], None, None]:
|
61
|
+
"""
|
62
|
+
Get completion from provider with tool calling support
|
63
|
+
|
64
|
+
Args:
|
65
|
+
messages: List of messages for the conversation
|
66
|
+
stream: Whether to stream the response
|
67
|
+
recursion_depth: Current recursion depth for tool calls
|
68
|
+
|
69
|
+
Yields:
|
70
|
+
LLMResponse objects and control signals
|
71
|
+
"""
|
72
|
+
if recursion_depth >= self.max_recursion_depth:
|
73
|
+
self.console.print(
|
74
|
+
f"Maximum recursion depth ({self.max_recursion_depth}) reached, stopping further tool calls",
|
75
|
+
style="yellow",
|
76
|
+
)
|
77
|
+
return
|
78
|
+
|
79
|
+
# Get completion from provider and collect response data
|
80
|
+
assistant_response_content = ""
|
81
|
+
# Providers may return identical tool calls with the same ID in a single response during streaming
|
82
|
+
tool_calls: dict[str, ToolCall] = {}
|
83
|
+
|
84
|
+
# Stream responses and collect data
|
85
|
+
for llm_response in self.provider.completion(messages, stream=stream):
|
86
|
+
yield llm_response # Forward response to caller
|
87
|
+
|
88
|
+
# Collect content and tool calls for potential tool execution
|
89
|
+
if llm_response.content:
|
90
|
+
assistant_response_content += llm_response.content
|
91
|
+
if llm_response.tool_call and llm_response.tool_call.id not in tool_calls:
|
92
|
+
tool_calls[llm_response.tool_call.id] = llm_response.tool_call
|
93
|
+
|
94
|
+
# Check if we need to execute tools
|
95
|
+
if not tool_calls or not (self.enable_function or self.enable_mcp):
|
96
|
+
return
|
97
|
+
|
98
|
+
# Filter valid tool calls based on enabled features
|
99
|
+
valid_tool_calls = self._get_valid_tool_calls(tool_calls)
|
100
|
+
if not valid_tool_calls:
|
101
|
+
return
|
102
|
+
|
103
|
+
# Execute tools and continue conversation
|
104
|
+
yield from self._execute_tools_and_continue(
|
105
|
+
messages, assistant_response_content, valid_tool_calls, stream, recursion_depth
|
106
|
+
)
|
107
|
+
|
108
|
+
def _get_valid_tool_calls(self, tool_calls: dict[str, ToolCall]) -> List[ToolCall]:
|
109
|
+
"""Filter tool calls based on enabled features"""
|
110
|
+
valid_tool_calls = []
|
111
|
+
|
112
|
+
for tool_call in tool_calls.values():
|
113
|
+
is_mcp = tool_call.name.startswith(MCP_TOOL_NAME_PREFIX)
|
114
|
+
|
115
|
+
if is_mcp and self.enable_mcp:
|
116
|
+
valid_tool_calls.append(tool_call)
|
117
|
+
elif not is_mcp and self.enable_function:
|
118
|
+
valid_tool_calls.append(tool_call)
|
119
|
+
|
120
|
+
return valid_tool_calls
|
121
|
+
|
122
|
+
def _execute_tools_and_continue(
|
123
|
+
self,
|
124
|
+
messages: List[ChatMessage],
|
125
|
+
assistant_response_content: str,
|
126
|
+
tool_calls: List[ToolCall],
|
127
|
+
stream: bool,
|
128
|
+
recursion_depth: int,
|
129
|
+
) -> Generator[Union[LLMResponse, RefreshLive], None, None]:
|
130
|
+
"""Execute tool calls and continue the conversation"""
|
131
|
+
# Signal that new content is coming
|
132
|
+
yield RefreshLive()
|
133
|
+
|
134
|
+
# Add assistant message with tool calls to history (only once)
|
135
|
+
messages.append(ChatMessage(role="assistant", content=assistant_response_content, tool_calls=tool_calls))
|
136
|
+
|
137
|
+
# Execute each tool call and add results to messages
|
138
|
+
tool_role = self.provider.detect_tool_role()
|
139
|
+
|
140
|
+
for tool_call in tool_calls:
|
141
|
+
function_result, _ = execute_tool_call(tool_call)
|
142
|
+
|
143
|
+
messages.append(
|
144
|
+
ChatMessage(
|
145
|
+
role=tool_role,
|
146
|
+
content=function_result,
|
147
|
+
name=tool_call.name,
|
148
|
+
tool_call_id=tool_call.id,
|
149
|
+
)
|
150
|
+
)
|
151
|
+
|
152
|
+
# Continue the conversation with updated history
|
153
|
+
yield from self.completion_with_tools(messages, stream=stream, recursion_depth=recursion_depth + 1)
|
@@ -63,7 +63,7 @@ class AI21Provider(OpenAIProvider):
|
|
63
63
|
if finish_reason == "tool_calls" and not content:
|
64
64
|
# tool call assistant message, content can't be empty
|
65
65
|
# Error code: 422 - {'detail': {'error': ['Value error, message content must not be an empty string']}}
|
66
|
-
content = tool_call.id
|
66
|
+
content = tool_call.id if tool_call else ""
|
67
67
|
|
68
68
|
# Generate response object
|
69
69
|
yield LLMResponse(
|
@@ -72,3 +72,35 @@ class AI21Provider(OpenAIProvider):
|
|
72
72
|
tool_call=tool_call if finish_reason == "tool_calls" else None,
|
73
73
|
finish_reason=finish_reason,
|
74
74
|
)
|
75
|
+
|
76
|
+
def _process_tool_call_chunk(self, tool_calls, existing_tool_call=None):
|
77
|
+
"""Process a tool call chunk from AI21 API response
|
78
|
+
|
79
|
+
Tool calls from AI21 are delivered across multiple chunks:
|
80
|
+
- First chunk contains function name
|
81
|
+
- Subsequent chunks contain arguments data
|
82
|
+
- Final chunk (with finish_reason='tool_calls') contains complete arguments
|
83
|
+
|
84
|
+
Args:
|
85
|
+
tool_calls: Tool call data from current chunk
|
86
|
+
existing_tool_call: Previously accumulated tool call object
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
ToolCall: Updated tool call object with accumulated data
|
90
|
+
"""
|
91
|
+
# Get the first (and only) tool call from the chunk
|
92
|
+
call = tool_calls[0]
|
93
|
+
|
94
|
+
if existing_tool_call is None:
|
95
|
+
# First chunk - create new tool call with function name
|
96
|
+
return ToolCall(id=call.id, name=call.function.name, arguments="{}")
|
97
|
+
else:
|
98
|
+
# Update existing tool call with new arguments data
|
99
|
+
# Keep existing data and update with new information
|
100
|
+
existing_arguments = existing_tool_call.arguments
|
101
|
+
new_arguments = call.function.arguments if hasattr(call.function, "arguments") else "{}"
|
102
|
+
|
103
|
+
# Combine arguments (new arguments should override if available)
|
104
|
+
combined_arguments = new_arguments if new_arguments else existing_arguments
|
105
|
+
|
106
|
+
return ToolCall(id=existing_tool_call.id, name=existing_tool_call.name, arguments=combined_arguments)
|