yaicli 0.6.4__tar.gz → 0.7.1__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.
Files changed (50) hide show
  1. {yaicli-0.6.4 → yaicli-0.7.1}/PKG-INFO +94 -4
  2. {yaicli-0.6.4 → yaicli-0.7.1}/README.md +90 -3
  3. {yaicli-0.6.4 → yaicli-0.7.1}/pyproject.toml +5 -1
  4. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/const.py +17 -6
  5. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/entry.py +24 -1
  6. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/functions/__init__.py +13 -1
  7. yaicli-0.7.1/yaicli/llms/client.py +153 -0
  8. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/ai21_provider.py +33 -1
  9. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/chatglm_provider.py +30 -7
  10. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/doubao_provider.py +0 -14
  11. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/gemini_provider.py +21 -22
  12. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/huggingface_provider.py +1 -1
  13. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/openai_provider.py +17 -8
  14. yaicli-0.7.1/yaicli/tools/__init__.py +127 -0
  15. yaicli-0.7.1/yaicli/tools/function.py +90 -0
  16. yaicli-0.7.1/yaicli/tools/mcp.py +459 -0
  17. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/utils.py +34 -0
  18. yaicli-0.6.4/yaicli/llms/client.py +0 -120
  19. yaicli-0.6.4/yaicli/tools.py +0 -159
  20. {yaicli-0.6.4 → yaicli-0.7.1}/.gitignore +0 -0
  21. {yaicli-0.6.4 → yaicli-0.7.1}/LICENSE +0 -0
  22. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/__init__.py +0 -0
  23. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/chat.py +0 -0
  24. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/cli.py +0 -0
  25. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/config.py +0 -0
  26. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/console.py +0 -0
  27. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/exceptions.py +0 -0
  28. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/functions/buildin/execute_shell_command.py +0 -0
  29. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/history.py +0 -0
  30. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/__init__.py +0 -0
  31. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/provider.py +0 -0
  32. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/chutes_provider.py +0 -0
  33. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/cohere_provider.py +0 -0
  34. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/deepseek_provider.py +0 -0
  35. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/groq_provider.py +0 -0
  36. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/infiniai_provider.py +0 -0
  37. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/minimax_provider.py +0 -0
  38. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/modelscope_provider.py +0 -0
  39. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/ollama_provider.py +0 -0
  40. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/openrouter_provider.py +0 -0
  41. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/sambanova_provider.py +0 -0
  42. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/siliconflow_provider.py +0 -0
  43. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/targon_provider.py +0 -0
  44. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/vertexai_provider.py +0 -0
  45. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/xai_provider.py +0 -0
  46. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/llms/providers/yi_provider.py +0 -0
  47. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/printer.py +0 -0
  48. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/render.py +0 -0
  49. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/role.py +0 -0
  50. {yaicli-0.6.4 → yaicli-0.7.1}/yaicli/schemas.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yaicli
3
- Version: 0.6.4
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 in response | `true` | `YAI_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=sambanova
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
+ ![mcp](artwork/mcp_example.png)
1343
+
1344
+
1255
1345
  ## 💻 Technical Details
1256
1346
 
1257
1347
  ### 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 in response | `true` | `YAI_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=sambanova
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
+ ![mcp](artwork/mcp_example.png)
1097
+
1098
+
1012
1099
  ## 💻 Technical Details
1013
1100
 
1014
1101
  ### Architecture
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "yaicli"
3
- version = "0.6.4"
3
+ version = "0.7.1"
4
4
  description = "A simple CLI tool to interact with LLM"
5
5
  authors = [{ name = "belingud", email = "im.victor@qq.com" }]
6
6
  readme = "README.md"
@@ -55,6 +55,7 @@ keywords = [
55
55
  dependencies = [
56
56
  "click>=8.1.8",
57
57
  "distro>=1.9.0",
58
+ "fastmcp>=2.9.2",
58
59
  "httpx>=0.28.1",
59
60
  "instructor>=1.7.9",
60
61
  "json-repair>=0.44.1",
@@ -88,6 +89,9 @@ gemini = ["google-genai>=1.20.0"]
88
89
  huggingface = [
89
90
  "huggingface-hub>=0.33.0",
90
91
  ]
92
+ mistral = [
93
+ "mistralai>=1.8.2",
94
+ ]
91
95
 
92
96
  [tool.pytest.ini_options]
93
97
  testpaths = ["tests"]
@@ -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 in the response
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)