naas-abi-core 1.0.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.
Files changed (124) hide show
  1. naas_abi_core/__init__.py +1 -0
  2. naas_abi_core/apps/api/api.py +242 -0
  3. naas_abi_core/apps/api/api_test.py +281 -0
  4. naas_abi_core/apps/api/openapi_doc.py +307 -0
  5. naas_abi_core/apps/mcp/mcp_server.py +243 -0
  6. naas_abi_core/apps/mcp/mcp_server_test.py +163 -0
  7. naas_abi_core/apps/terminal_agent/main.py +555 -0
  8. naas_abi_core/apps/terminal_agent/terminal_style.py +175 -0
  9. naas_abi_core/cli/__init__.py +53 -0
  10. naas_abi_core/cli/agent.py +30 -0
  11. naas_abi_core/cli/chat.py +26 -0
  12. naas_abi_core/cli/config.py +49 -0
  13. naas_abi_core/cli/init.py +13 -0
  14. naas_abi_core/cli/module.py +28 -0
  15. naas_abi_core/cli/new.py +13 -0
  16. naas_abi_core/cli/secret.py +79 -0
  17. naas_abi_core/engine/Engine.py +87 -0
  18. naas_abi_core/engine/EngineProxy.py +109 -0
  19. naas_abi_core/engine/Engine_test.py +6 -0
  20. naas_abi_core/engine/IEngine.py +91 -0
  21. naas_abi_core/engine/conftest.py +45 -0
  22. naas_abi_core/engine/engine_configuration/EngineConfiguration.py +160 -0
  23. naas_abi_core/engine/engine_configuration/EngineConfiguration_GenericLoader.py +49 -0
  24. naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService.py +131 -0
  25. naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService_test.py +26 -0
  26. naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService.py +116 -0
  27. naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService.py +171 -0
  28. naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService.py +65 -0
  29. naas_abi_core/engine/engine_configuration/EngineConfiguration_test.py +9 -0
  30. naas_abi_core/engine/engine_configuration/utils/PydanticModelValidator.py +15 -0
  31. naas_abi_core/engine/engine_loaders/EngineModuleLoader.py +302 -0
  32. naas_abi_core/engine/engine_loaders/EngineOntologyLoader.py +16 -0
  33. naas_abi_core/engine/engine_loaders/EngineServiceLoader.py +47 -0
  34. naas_abi_core/integration/__init__.py +7 -0
  35. naas_abi_core/integration/integration.py +28 -0
  36. naas_abi_core/models/Model.py +198 -0
  37. naas_abi_core/models/OpenRouter.py +15 -0
  38. naas_abi_core/models/OpenRouter_test.py +36 -0
  39. naas_abi_core/module/Module.py +245 -0
  40. naas_abi_core/module/ModuleAgentLoader.py +49 -0
  41. naas_abi_core/module/ModuleUtils.py +20 -0
  42. naas_abi_core/modules/templatablesparqlquery/README.md +196 -0
  43. naas_abi_core/modules/templatablesparqlquery/__init__.py +39 -0
  44. naas_abi_core/modules/templatablesparqlquery/ontologies/TemplatableSparqlQueryOntology.ttl +116 -0
  45. naas_abi_core/modules/templatablesparqlquery/workflows/GenericWorkflow.py +48 -0
  46. naas_abi_core/modules/templatablesparqlquery/workflows/TemplatableSparqlQueryLoader.py +192 -0
  47. naas_abi_core/pipeline/__init__.py +6 -0
  48. naas_abi_core/pipeline/pipeline.py +70 -0
  49. naas_abi_core/services/__init__.py +0 -0
  50. naas_abi_core/services/agent/Agent.py +1619 -0
  51. naas_abi_core/services/agent/AgentMemory_test.py +28 -0
  52. naas_abi_core/services/agent/Agent_test.py +214 -0
  53. naas_abi_core/services/agent/IntentAgent.py +1171 -0
  54. naas_abi_core/services/agent/IntentAgent_test.py +139 -0
  55. naas_abi_core/services/agent/beta/Embeddings.py +180 -0
  56. naas_abi_core/services/agent/beta/IntentMapper.py +119 -0
  57. naas_abi_core/services/agent/beta/LocalModel.py +88 -0
  58. naas_abi_core/services/agent/beta/VectorStore.py +89 -0
  59. naas_abi_core/services/agent/test_agent_memory.py +278 -0
  60. naas_abi_core/services/agent/test_postgres_integration.py +145 -0
  61. naas_abi_core/services/cache/CacheFactory.py +31 -0
  62. naas_abi_core/services/cache/CachePort.py +63 -0
  63. naas_abi_core/services/cache/CacheService.py +246 -0
  64. naas_abi_core/services/cache/CacheService_test.py +85 -0
  65. naas_abi_core/services/cache/adapters/secondary/CacheFSAdapter.py +39 -0
  66. naas_abi_core/services/object_storage/ObjectStorageFactory.py +57 -0
  67. naas_abi_core/services/object_storage/ObjectStoragePort.py +47 -0
  68. naas_abi_core/services/object_storage/ObjectStorageService.py +41 -0
  69. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterFS.py +52 -0
  70. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterNaas.py +131 -0
  71. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterS3.py +171 -0
  72. naas_abi_core/services/ontology/OntologyPorts.py +36 -0
  73. naas_abi_core/services/ontology/OntologyService.py +17 -0
  74. naas_abi_core/services/ontology/adaptors/secondary/OntologyService_SecondaryAdaptor_NERPort.py +37 -0
  75. naas_abi_core/services/secret/Secret.py +138 -0
  76. naas_abi_core/services/secret/SecretPorts.py +40 -0
  77. naas_abi_core/services/secret/Secret_test.py +65 -0
  78. naas_abi_core/services/secret/adaptors/secondary/Base64Secret.py +57 -0
  79. naas_abi_core/services/secret/adaptors/secondary/Base64Secret_test.py +39 -0
  80. naas_abi_core/services/secret/adaptors/secondary/NaasSecret.py +81 -0
  81. naas_abi_core/services/secret/adaptors/secondary/NaasSecret_test.py +25 -0
  82. naas_abi_core/services/secret/adaptors/secondary/dotenv_secret_secondaryadaptor.py +26 -0
  83. naas_abi_core/services/triple_store/TripleStoreFactory.py +116 -0
  84. naas_abi_core/services/triple_store/TripleStorePorts.py +223 -0
  85. naas_abi_core/services/triple_store/TripleStoreService.py +419 -0
  86. naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune.py +1284 -0
  87. naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune_test.py +284 -0
  88. naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph.py +597 -0
  89. naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph_test.py +1474 -0
  90. naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__Filesystem.py +223 -0
  91. naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__ObjectStorage.py +234 -0
  92. naas_abi_core/services/triple_store/adaptors/secondary/base/TripleStoreService__SecondaryAdaptor__FileBase.py +18 -0
  93. naas_abi_core/services/vector_store/IVectorStorePort.py +101 -0
  94. naas_abi_core/services/vector_store/IVectorStorePort_test.py +189 -0
  95. naas_abi_core/services/vector_store/VectorStoreFactory.py +47 -0
  96. naas_abi_core/services/vector_store/VectorStoreService.py +171 -0
  97. naas_abi_core/services/vector_store/VectorStoreService_test.py +185 -0
  98. naas_abi_core/services/vector_store/__init__.py +13 -0
  99. naas_abi_core/services/vector_store/adapters/QdrantAdapter.py +251 -0
  100. naas_abi_core/services/vector_store/adapters/QdrantAdapter_test.py +57 -0
  101. naas_abi_core/utils/Expose.py +53 -0
  102. naas_abi_core/utils/Graph.py +182 -0
  103. naas_abi_core/utils/JSON.py +49 -0
  104. naas_abi_core/utils/LazyLoader.py +44 -0
  105. naas_abi_core/utils/Logger.py +12 -0
  106. naas_abi_core/utils/OntologyReasoner.py +141 -0
  107. naas_abi_core/utils/OntologyYaml.disabled.py +679 -0
  108. naas_abi_core/utils/SPARQL.py +256 -0
  109. naas_abi_core/utils/Storage.py +33 -0
  110. naas_abi_core/utils/StorageUtils.py +398 -0
  111. naas_abi_core/utils/String.py +52 -0
  112. naas_abi_core/utils/Workers.py +114 -0
  113. naas_abi_core/utils/__init__.py +0 -0
  114. naas_abi_core/utils/onto2py/README.md +0 -0
  115. naas_abi_core/utils/onto2py/__init__.py +10 -0
  116. naas_abi_core/utils/onto2py/__main__.py +29 -0
  117. naas_abi_core/utils/onto2py/onto2py.py +611 -0
  118. naas_abi_core/utils/onto2py/tests/ttl2py_test.py +271 -0
  119. naas_abi_core/workflow/__init__.py +5 -0
  120. naas_abi_core/workflow/workflow.py +48 -0
  121. naas_abi_core-1.0.0.dist-info/METADATA +75 -0
  122. naas_abi_core-1.0.0.dist-info/RECORD +124 -0
  123. naas_abi_core-1.0.0.dist-info/WHEEL +4 -0
  124. naas_abi_core-1.0.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,307 @@
1
+ TAGS_METADATA = [
2
+ {
3
+ "name": "Overview",
4
+ "description": """
5
+ ### Project Overview
6
+ The **ABI** (Artificial Business Intelligence) project is a Python-based backend framework designed to serve as the core infrastructure for building an Organizational AI System.
7
+ This system empowers businesses to integrate, manage, and scale AI-driven operations with a focus on ontology, assistant-driven workflows, and analytics.\n
8
+ Designed for flexibility and scalability, ABI provides a customizable framework suitable for organizations aiming to create intelligent, automated systems tailored to their needs.
9
+
10
+ ### API Overview
11
+ The ABI API allows users and applications to interact with ABI's capabilities for business process automation and intelligence.\n
12
+ This document describes the current version of the ABI API, which provides access to agents, pipelines, workflows, integrations, ontology management and analytics features.
13
+ """,
14
+ },
15
+ {
16
+ "name": "Authentication",
17
+ "description": """
18
+ Authentication uses a Bearer token that can be provided either in the Authorization header (e.g. 'Authorization: Bearer `<token>`') or as a query parameter (e.g. '?token=`<token>`').
19
+ The token must match the `ABI_API_KEY` environment variable.
20
+ Contact your administrator to get the token.
21
+
22
+ *Authentication with Authorization header:*
23
+
24
+ ```python
25
+ import requests
26
+
27
+ url = "https://<your-registry-name>.default.space.naas.ai/agents/abi/completion"
28
+
29
+ headers = {
30
+ "Authorization": f"Bearer {token}"
31
+ }
32
+
33
+ response = requests.post(url, headers=headers)
34
+ print(response.json())
35
+ ```
36
+
37
+ *Authentication with query parameter:*
38
+
39
+ ```python
40
+ import requests
41
+
42
+ url = "https://<your-registry-name>.default.space.naas.ai/agents/abi/completion?token=<token>"
43
+
44
+ response = requests.post(url)
45
+ print(response.json())
46
+ ```
47
+ """,
48
+ },
49
+ {
50
+ "name": "Connections",
51
+ "description": """
52
+ Connections are currently configured using Integration secrets (API keys, credentials) set up in the GitHub project settings. \n
53
+ Learn more about the data access and secrets key [here](https://github.com/jupyter-naas/abi/blob/main/README.md#setup-github-repository-secrets).
54
+
55
+ ### Agicap
56
+ Required:
57
+ - `AGICAP_USERNAME`: Username of your Agicap account
58
+ - `AGICAP_PASSWORD`: Password of your Agicap account
59
+ - `AGICAP_CLIENT_ID`: Client ID of your Agicap account. [Get your client ID](https://app.agicap.com/fr/app/organization-advanced-settings/public-api)
60
+ - `AGICAP_CLIENT_SECRET`: Client Secret of your Agicap account. [Get your client secret](https://app.agicap.com/fr/app/organization-advanced-settings/public-api)
61
+ - `AGICAP_BEARER_TOKEN`: Bearer Token of your Agicap account.
62
+ - `AGICAP_API_TOKEN`: API token of your Agicap account. [Get your API token](https://app.agicap.com/fr/app/parametres/openapi)
63
+
64
+ ### Algolia
65
+ Required:
66
+ - `ALGOLIA_API_KEY`: API key of your Algolia account. [Get your API key](https://www.algolia.com/api-keys)
67
+ - `ALGOLIA_APPLICATION_ID`: Application ID of your Algolia account. [Get your application ID](https://www.algolia.com/api-keys)
68
+
69
+ ### Airtable
70
+ Required:
71
+ - `AIRTABLE_API_KEY`: API key of your Airtable account. [Get your API key](https://airtable.com/create/tokens)
72
+ - `AIRTABLE_BASE_ID`: Base ID of your Airtable account. [Get your base ID](https://airtable.com/create/tokens)
73
+
74
+ ### AWS
75
+ Required:
76
+ - `AWS_ACCESS_KEY_ID`: Access key ID of your AWS account. [Get your access key ID](https://us-east-1.console.aws.amazon.com/iam/home)
77
+ - `AWS_SECRET_ACCESS_KEY`: Secret access key of your AWS account. [Get your secret access key](https://us-east-1.console.aws.amazon.com/iam/home)
78
+ - `AWS_REGION`: Region of your AWS account. [Get your region](https://us-east-1.console.aws.amazon.com/iam/home)
79
+
80
+ ### Brevo
81
+ Required:
82
+ - `BREVO_API_KEY`: API key of your Brevo account. [Get your API key](https://help.brevo.com/hc/en-us/articles/209467485)
83
+
84
+ ### Clockify
85
+ Required:
86
+ - `CLOCKIFY_API_KEY`: API key of your Clockify account. [Get your API key](https://clockify.me/user/settings)
87
+
88
+ ### Discord
89
+ Required:
90
+ - `DISCORD_API_KEY`: API key of your Discord account. [Get your API key](https://discord.com/developers/applications)
91
+
92
+ ### Github
93
+ Required:
94
+ - `GITHUB_ACCESS_TOKEN`: Access token of your Github account. [Get your access token](https://github.com/settings/tokens)
95
+
96
+ ### Gladia
97
+ Required:
98
+ - `GLADIA_API_KEY`: API key of your Gladia account. [Get your API key](https://www.gladia.ai/)
99
+
100
+ ### Gmail
101
+ Required:
102
+ - `GMAIL_EMAIL`: Email of your Gmail account.
103
+ - `GMAIL_APP_PASSWORD`: App password of your Gmail account. [Get your app password](https://support.google.com/mail/answer/185833)
104
+
105
+ ### Harvest
106
+ Required:
107
+ - `HARVEST_API_KEY`: API key of your Harvest account. [Get your API key](https://app.harvestapp.com/account/api)
108
+ - `HARVEST_ACCOUNT_ID`: Account ID of your Harvest account.
109
+
110
+ ### HubSpot
111
+ Required:
112
+ - `HUBSPOT_ACCESS_TOKEN`: Access token of your HubSpot account. [Get your access token](https://developers.hubspot.com/docs/api/private-apps)
113
+
114
+ ### Instagram
115
+ Required:
116
+ - `INSTAGRAM_ACCESS_TOKEN`: Access token of your Instagram account. [Get your access token](https://developers.facebook.com/docs/instagram-api)
117
+
118
+ ### LinkedIn
119
+ Required:
120
+ - `li_at` cookie: li_at cookie of your LinkedIn account. [Get your li_at cookie](https://www.notion.so/LinkedIn-driver-Get-your-cookies)
121
+ - `JSESSIONID` cookie: JSESSIONID cookie of your LinkedIn account. [Get your JSESSIONID cookie](https://www.notion.so/LinkedIn-driver-Get-your-cookies)
122
+
123
+ ### Mailchimp
124
+ Required:
125
+ - `MAILCHIMP_API_KEY`: API key of your Mailchimp account. [Get your API key](https://mailchimp.com/developer/api/)
126
+ - `MAILCHIMP_SERVER_PREFIX`: Server prefix of your Mailchimp account. [Get your server prefix](https://mailchimp.com/developer/api/)
127
+
128
+ ### Mercury
129
+ Required:
130
+ - `MERCURY_API_TOKEN`: API token of your Mercury account. [Get your API token](https://app.mercury.com/settings/tokens)
131
+
132
+ ### NewsAPI
133
+ Required:
134
+ - `NEWSAPI_API_KEY`: API key of your NewsAPI account. [Get your API key](https://newsapi.org/register)
135
+
136
+ ### Notion
137
+ Required:
138
+ - `NOTION_API_KEY`: API key of your Notion account. [Get your API key](https://www.notion.so/my-integrations)
139
+
140
+ ### OneDrive
141
+ Required:
142
+ - `ONEDRIVE_ACCESS_TOKEN`: Access token of your OneDrive account. [Get your access token](https://learn.microsoft.com/en-us/onedrive/developer/rest-api/getting-started/)
143
+
144
+ ### Pennylane
145
+ Required:
146
+ - `PENNYLANE_API_TOKEN`: API token of your Pennylane account. [Get your API token](https://pennylane.readme.io/docs/get-my-api-token)
147
+
148
+ ### Perplexity
149
+ Required:
150
+ - `PERPLEXITY_API_KEY`: API key of your Perplexity account. [Get your API key](https://docs.perplexity.ai/docs/getting-started)
151
+
152
+ ### Pipedrive
153
+ Required:
154
+ - `PIPEDRIVE_API_KEY`: API key of your Pipedrive account. [Get your API key](https://app.pipedrive.com/settings/api)
155
+
156
+ ### PostgreSQL
157
+ Required:
158
+ - `POSTGRES_HOST`: Host of your PostgreSQL database.
159
+ - `POSTGRES_PORT`: Port of your PostgreSQL database.
160
+ - `POSTGRES_DB`: Database name of your PostgreSQL database.
161
+ - `POSTGRES_USER`: User of your PostgreSQL database.
162
+ - `POSTGRES_PASSWORD`: Password of your PostgreSQL database.
163
+
164
+ ### Qonto
165
+ Required:
166
+ - `QONTO_ORGANIZATION_SLUG`: Organization slug of your Qonto account. [Get your organization slug](https://support-fr.qonto.com/hc/en-us/articles/23947692362513)
167
+ - `QONTO_SECRET_KEY`: Secret key of your Qonto account. [Get your secret key](https://support-fr.qonto.com/hc/en-us/articles/23947692362513)
168
+
169
+ ### Replicate
170
+ Required:
171
+ - `REPLICATE_API_KEY`: API key of your Replicate account. [Get your API key](https://replicate.com/account/api-tokens)
172
+
173
+ ### Serper
174
+ Required:
175
+ - `SERPER_API_KEY`: API key of your Serper account. [Get your API key](https://serper.dev/api-key)
176
+
177
+ ### Slack
178
+ Required:
179
+ - `SLACK_BOT_TOKEN`: Bot token of your Slack account. [Get your bot token](https://api.slack.com/apps)
180
+
181
+ ### Stripe
182
+ Required:
183
+ - `STRIPE_API_KEY`: API key of your Stripe account. [Get your API key](https://dashboard.stripe.com/apikeys)
184
+
185
+ ### Supabase
186
+ Required:
187
+ - `SUPABASE_URL`: URL of your Supabase account. [Get your URL](https://app.supabase.com/project/_/settings/api)
188
+ - `SUPABASE_KEY`: Key of your Supabase account. [Get your key](https://app.supabase.com/project/_/settings/api)
189
+
190
+ ### TikTok
191
+ Required:
192
+ - `TIKTOK_ACCESS_TOKEN`: Access token of your TikTok account. [Get your access token](https://developers.tiktok.com/)
193
+
194
+ ### WhatsApp
195
+ Required:
196
+ - `WHATSAPP_ACCESS_TOKEN`: Access token of your WhatsApp account. [Get your access token](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started)
197
+
198
+ ### Yahoo Finance
199
+ Required:
200
+ - `YAHOO_FINANCE_API_KEY`: API key of your Yahoo Finance account. [Get your API key](https://www.yahoofinanceapi.com/)
201
+
202
+ ### YouTube
203
+ Required:
204
+ - `YOUTUBE_API_KEY`: API key of your YouTube account. [Get your API key](https://console.cloud.google.com/apis/credentials)
205
+
206
+ ### ZeroBounce
207
+ Required:
208
+ - `ZEROBOUNCE_API_KEY`: API key of your ZeroBounce account. [Get your API key](https://app.zerobounce.net/)
209
+
210
+ """,
211
+ },
212
+ {
213
+ "name": "Agents",
214
+ "description": """
215
+ API endpoints for interacting with ABI's agents.
216
+
217
+ ### Core Agents:
218
+ - Abi: Manages and coordinates other agents
219
+ - Ontology: Manages and coordinates other agents
220
+ - Naas: Manages and coordinates other agents
221
+ - Support: Provides help and guidance for using ABI
222
+
223
+ ### Marketplace Agents:
224
+ - Custom agents with deep expertise in specific domains
225
+ - Can be configured and trained for specialized tasks
226
+ - Extensible through custom tools and knowledge bases
227
+
228
+ Each agent can be accessed through dedicated endpoints that allow:
229
+ - Completion requests for generating responses
230
+ - Chat interactions for ongoing conversations
231
+ - Tool execution for specific tasks
232
+ - Configuration updates for customizing behavior
233
+
234
+ Agents leverage various tools including integrations, pipelines and workflows to accomplish tasks. They can be extended with custom tools and knowledge to enhance their capabilities.
235
+
236
+ """,
237
+ },
238
+ {
239
+ "name": "Pipelines",
240
+ "description": """
241
+ API endpoints for interacting with ABI's pipelines.
242
+ """,
243
+ },
244
+ {
245
+ "name": "Workflows",
246
+ "description": """
247
+ API endpoints for interacting with ABI's workflows.
248
+ """,
249
+ },
250
+ ]
251
+
252
+ API_LANDING_HTML = """
253
+ <!DOCTYPE html>
254
+ <html>
255
+ <head>
256
+ <title>[TITLE]</title>
257
+ <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
258
+ <style>
259
+ body {
260
+ font-family: Arial, sans-serif;
261
+ display: flex;
262
+ flex-direction: column;
263
+ align-items: center;
264
+ justify-content: center;
265
+ height: 100vh;
266
+ margin: 0;
267
+ background-color: #000000;
268
+ color: white;
269
+ }
270
+ .logo {
271
+ width: 200px;
272
+ margin-bottom: 20px;
273
+ }
274
+ h1 {
275
+ font-size: 48px;
276
+ margin-bottom: 40px;
277
+ }
278
+ .buttons {
279
+ display: flex;
280
+ gap: 20px;
281
+ }
282
+ a {
283
+ padding: 12px 24px;
284
+ font-size: 18px;
285
+ border: none;
286
+ border-radius: 4px;
287
+ cursor: pointer;
288
+ text-decoration: none;
289
+ color: white;
290
+ background-color: #007bff;
291
+ transition: background-color 0.2s;
292
+ }
293
+ a:hover {
294
+ background-color: #0056b3;
295
+ }
296
+ </style>
297
+ </head>
298
+ <body>
299
+ <img src="/static/[LOGO_NAME]" alt="Logo" class="logo">
300
+ <h1>Welcome to [TITLE]!</h1>
301
+ <p>[TITLE] is a tool that allows you to interact with ABI's capabilities for business process automation and intelligence.</p>
302
+ <div class="buttons">
303
+ <a href="/redoc">Go to Documentation</a>
304
+ </div>
305
+ </body>
306
+ </html>
307
+ """
@@ -0,0 +1,243 @@
1
+ """
2
+ ABI MCP Server - Lightweight HTTP-based Implementation
3
+ Exposes ABI agents as MCP tools with fast startup (no heavy imports)
4
+ """
5
+
6
+ import asyncio
7
+ import os
8
+ import re
9
+ from typing import Any, Dict, List
10
+
11
+ import httpx
12
+ from dotenv import load_dotenv
13
+ from mcp.server.fastmcp import FastMCP
14
+
15
+ # Load environment variables from .env file if present
16
+ load_dotenv()
17
+
18
+ # Initialize FastMCP server
19
+ mcp = FastMCP("abi")
20
+
21
+ # Constants - Default to localhost for development, can be overridden by env var
22
+ ABI_API_BASE = os.environ.get("ABI_API_BASE", "http://localhost:9879")
23
+
24
+
25
+ def get_api_key() -> str:
26
+ """Get the API key from environment variables"""
27
+ api_key = os.environ.get("ABI_API_KEY")
28
+ if not api_key:
29
+ print("❌ ABI_API_KEY not found in environment")
30
+ print("📝 Please add it to your .env file:")
31
+ print(" ABI_API_KEY=your_key_here")
32
+ exit(1)
33
+ return api_key
34
+
35
+
36
+ async def fetch_openapi_spec() -> Dict[str, Any]:
37
+ """Fetch the OpenAPI specification to discover available agents"""
38
+ try:
39
+ async with httpx.AsyncClient(timeout=10.0) as client:
40
+ response = await client.get(f"{ABI_API_BASE}/openapi.json")
41
+ response.raise_for_status()
42
+ return response.json()
43
+ except Exception as e:
44
+ print(f"❌ Failed to fetch OpenAPI spec: {e}")
45
+ return {}
46
+
47
+
48
+ def extract_agents_from_openapi(openapi_spec: Dict[str, Any]) -> List[Dict[str, str]]:
49
+ """Extract agent information from OpenAPI specification"""
50
+ agents = []
51
+ paths = openapi_spec.get("paths", {})
52
+
53
+ for path, methods in paths.items():
54
+ # Look for agent completion endpoints
55
+ if "/agents/" in path and path.endswith("/completion"):
56
+ # Extract agent name from path like /agents/{agent_name}/completion
57
+ agent_name = path.split("/agents/")[1].split("/completion")[0]
58
+
59
+ # Get description from POST method
60
+ post_method = methods.get("post", {})
61
+ description = post_method.get("summary", f"{agent_name} agent completion")
62
+
63
+ agents.append(
64
+ {
65
+ "name": agent_name,
66
+ "description": description,
67
+ "function_name": agent_name_to_function_name(agent_name),
68
+ }
69
+ )
70
+
71
+ return agents
72
+
73
+
74
+ def agent_name_to_function_name(agent_name: str) -> str:
75
+ """Convert agent name to valid Python function name"""
76
+ # Replace spaces and special chars with underscores, convert to lowercase
77
+ function_name = re.sub(r"[^a-zA-Z0-9_]", "_", agent_name.lower())
78
+ # Remove multiple consecutive underscores
79
+ function_name = re.sub(r"_+", "_", function_name)
80
+ # Remove leading/trailing underscores
81
+ function_name = function_name.strip("_")
82
+ # Ensure it doesn't start with a number
83
+ if function_name and function_name[0].isdigit():
84
+ function_name = f"agent_{function_name}"
85
+ return function_name or "unknown_agent"
86
+
87
+
88
+ async def call_abi_agent_http(agent_name: str, prompt: str, thread_id: int = 1) -> str:
89
+ """Call ABI agents via HTTP to avoid heavy module imports"""
90
+ try:
91
+ headers = {
92
+ "Authorization": f"Bearer {get_api_key()}",
93
+ "Content-Type": "application/json",
94
+ "User-Agent": "ABI-MCP/1.0",
95
+ }
96
+
97
+ data = {"prompt": prompt, "thread_id": thread_id}
98
+
99
+ url = f"{ABI_API_BASE}/agents/{agent_name}/completion"
100
+
101
+ async with httpx.AsyncClient(timeout=30.0) as client:
102
+ response = await client.post(url, json=data, headers=headers)
103
+ response.raise_for_status()
104
+ return response.text.strip('"') # Remove JSON quotes
105
+
106
+ except httpx.ConnectError:
107
+ return f"❌ ABI API server not running at {ABI_API_BASE}. Please start it first with: uv run api"
108
+ except httpx.TimeoutException:
109
+ return f"⏱️ Timeout calling {agent_name} agent. The agent might be processing a complex request."
110
+ except httpx.HTTPStatusError as e:
111
+ if e.response.status_code == 401:
112
+ return (
113
+ "🔒 Authentication failed. Check your ABI_API_KEY environment variable."
114
+ )
115
+ elif e.response.status_code == 404:
116
+ return f"❓ Agent '{agent_name}' not found. Please check available agents via OpenAPI spec."
117
+ else:
118
+ return f"❌ HTTP {e.response.status_code} error calling {agent_name} agent: {e.response.text}"
119
+ except Exception as e:
120
+ return f"❌ Error calling {agent_name} agent: {str(e)}"
121
+
122
+
123
+ def create_agent_function(agent_name: str, description: str):
124
+ """Create a dynamic agent function"""
125
+
126
+ async def agent_function(prompt: str, thread_id: int = 1) -> str:
127
+ return await call_abi_agent_http(agent_name, prompt, thread_id)
128
+
129
+ # Set function metadata
130
+ agent_function.__name__ = agent_name_to_function_name(agent_name)
131
+ agent_function.__doc__ = f"""{description}
132
+
133
+ Args:
134
+ prompt: Your question or request for the {agent_name} agent
135
+ thread_id: Thread ID for conversation context (default: 1)
136
+ """
137
+
138
+ return agent_function
139
+
140
+
141
+ async def wait_for_api():
142
+ """Wait for the API to be available before starting"""
143
+ max_retries = 30 # Wait up to 5 minutes (30 * 10 seconds)
144
+ retry_delay = 10 # seconds
145
+
146
+ for attempt in range(max_retries):
147
+ try:
148
+ async with httpx.AsyncClient(timeout=5.0) as client:
149
+ response = await client.get(f"{ABI_API_BASE}")
150
+ if response.status_code == 200:
151
+ print("✅ API is ready!")
152
+ return True
153
+ except Exception:
154
+ pass
155
+
156
+ if attempt < max_retries - 1:
157
+ print(
158
+ f"⏳ Waiting for API to be ready... (attempt {attempt + 1}/{max_retries})"
159
+ )
160
+ await asyncio.sleep(retry_delay)
161
+
162
+ print("❌ API did not become ready in time")
163
+ return False
164
+
165
+
166
+ async def register_agents_dynamically():
167
+ """Discover and register agents from OpenAPI specification"""
168
+ print("🔍 Discovering agents from OpenAPI specification...")
169
+
170
+ # Wait for API to be ready if not running locally
171
+ if not ABI_API_BASE.startswith("http://localhost"):
172
+ api_ready = await wait_for_api()
173
+ if not api_ready:
174
+ print("⚠️ API not ready, using fallback configuration")
175
+ return
176
+
177
+ openapi_spec = await fetch_openapi_spec()
178
+ if not openapi_spec:
179
+ print("⚠️ Failed to fetch OpenAPI spec, falling back to basic agents")
180
+ return
181
+
182
+ agents = extract_agents_from_openapi(openapi_spec)
183
+
184
+ if not agents:
185
+ print("⚠️ No agents found in OpenAPI spec")
186
+ return
187
+
188
+ print(f"📡 Found {len(agents)} agents:")
189
+
190
+ for agent_info in agents:
191
+ agent_name = agent_info["name"]
192
+ description = agent_info["description"]
193
+ function_name = agent_info["function_name"]
194
+
195
+ print(f" • {agent_name} -> {function_name}()")
196
+
197
+ # Create and register the agent function
198
+ agent_function = create_agent_function(agent_name, description)
199
+ mcp.tool()(agent_function)
200
+
201
+ print("✅ All agents registered successfully!")
202
+
203
+
204
+ async def setup():
205
+ """Setup function to initialize the server"""
206
+ # Quick startup - no heavy imports!
207
+ print("🚀 Starting lightweight ABI MCP Server...")
208
+ print(f"📡 Will connect to ABI API at: {ABI_API_BASE}")
209
+
210
+ # Validate API key exists
211
+ get_api_key()
212
+ print("🔑 API key loaded successfully")
213
+
214
+ # Dynamically discover and register agents
215
+ await register_agents_dynamically()
216
+
217
+
218
+ def run():
219
+ """Entry point for the script"""
220
+ # Run setup first
221
+ asyncio.run(setup())
222
+
223
+ # Determine transport type from environment
224
+ transport = os.environ.get("MCP_TRANSPORT", "stdio")
225
+
226
+ if transport == "sse":
227
+ # SSE transport for web deployment
228
+ print(
229
+ "🌐 Starting MCP server with SSE (Server-Sent Events) transport on port 8000"
230
+ )
231
+ mcp.run(transport="sse")
232
+ elif transport == "http":
233
+ # HTTP transport using streamable-http
234
+ print("🌐 Starting MCP server with streamable HTTP transport")
235
+ mcp.run(transport="streamable-http")
236
+ else:
237
+ # STDIO transport for local Claude Desktop integration
238
+ print("📋 Starting MCP server with STDIO transport")
239
+ mcp.run(transport="stdio")
240
+
241
+
242
+ if __name__ == "__main__":
243
+ run()