agno 2.3.16__py3-none-any.whl → 2.3.17__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 (75) hide show
  1. agno/agent/__init__.py +2 -0
  2. agno/agent/agent.py +4 -53
  3. agno/agent/remote.py +351 -0
  4. agno/client/__init__.py +3 -0
  5. agno/client/os.py +2669 -0
  6. agno/db/base.py +20 -0
  7. agno/db/mongo/async_mongo.py +11 -0
  8. agno/db/mongo/mongo.py +10 -0
  9. agno/db/mysql/async_mysql.py +9 -0
  10. agno/db/mysql/mysql.py +9 -0
  11. agno/db/postgres/async_postgres.py +9 -0
  12. agno/db/postgres/postgres.py +9 -0
  13. agno/db/postgres/utils.py +3 -2
  14. agno/db/sqlite/async_sqlite.py +9 -0
  15. agno/db/sqlite/sqlite.py +11 -1
  16. agno/exceptions.py +23 -0
  17. agno/knowledge/chunking/semantic.py +123 -46
  18. agno/knowledge/reader/csv_reader.py +1 -1
  19. agno/knowledge/reader/field_labeled_csv_reader.py +1 -1
  20. agno/knowledge/reader/json_reader.py +1 -1
  21. agno/os/app.py +104 -23
  22. agno/os/auth.py +25 -1
  23. agno/os/interfaces/a2a/a2a.py +7 -6
  24. agno/os/interfaces/a2a/router.py +13 -13
  25. agno/os/interfaces/agui/agui.py +5 -3
  26. agno/os/interfaces/agui/router.py +23 -16
  27. agno/os/interfaces/base.py +7 -7
  28. agno/os/interfaces/slack/router.py +6 -6
  29. agno/os/interfaces/slack/slack.py +7 -7
  30. agno/os/interfaces/whatsapp/router.py +29 -6
  31. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  32. agno/os/managers.py +326 -0
  33. agno/os/mcp.py +651 -79
  34. agno/os/router.py +125 -18
  35. agno/os/routers/agents/router.py +65 -22
  36. agno/os/routers/agents/schema.py +16 -4
  37. agno/os/routers/database.py +5 -0
  38. agno/os/routers/evals/evals.py +93 -11
  39. agno/os/routers/evals/utils.py +6 -6
  40. agno/os/routers/knowledge/knowledge.py +104 -16
  41. agno/os/routers/memory/memory.py +124 -7
  42. agno/os/routers/metrics/metrics.py +21 -4
  43. agno/os/routers/session/session.py +141 -12
  44. agno/os/routers/teams/router.py +40 -14
  45. agno/os/routers/teams/schema.py +12 -4
  46. agno/os/routers/traces/traces.py +54 -4
  47. agno/os/routers/workflows/router.py +223 -117
  48. agno/os/routers/workflows/schema.py +65 -1
  49. agno/os/schema.py +38 -12
  50. agno/os/utils.py +87 -166
  51. agno/remote/__init__.py +3 -0
  52. agno/remote/base.py +484 -0
  53. agno/run/workflow.py +1 -0
  54. agno/team/__init__.py +2 -0
  55. agno/team/remote.py +287 -0
  56. agno/team/team.py +25 -54
  57. agno/tracing/exporter.py +10 -6
  58. agno/tracing/setup.py +2 -1
  59. agno/utils/agent.py +58 -1
  60. agno/utils/http.py +68 -20
  61. agno/utils/os.py +0 -0
  62. agno/utils/remote.py +23 -0
  63. agno/vectordb/chroma/chromadb.py +452 -16
  64. agno/vectordb/pgvector/pgvector.py +7 -0
  65. agno/vectordb/redis/redisdb.py +1 -1
  66. agno/workflow/__init__.py +2 -0
  67. agno/workflow/agent.py +2 -2
  68. agno/workflow/remote.py +222 -0
  69. agno/workflow/types.py +0 -73
  70. agno/workflow/workflow.py +119 -68
  71. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/METADATA +1 -1
  72. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/RECORD +75 -65
  73. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/WHEEL +0 -0
  74. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/licenses/LICENSE +0 -0
  75. {agno-2.3.16.dist-info → agno-2.3.17.dist-info}/top_level.txt +0 -0
@@ -2,13 +2,13 @@ import logging
2
2
  from copy import deepcopy
3
3
  from typing import List, Optional, Union, cast
4
4
 
5
- from fastapi import APIRouter, Depends, HTTPException, Query
5
+ from fastapi import APIRouter, Depends, HTTPException, Query, Request
6
6
 
7
- from agno.agent.agent import Agent
7
+ from agno.agent import Agent, RemoteAgent
8
8
  from agno.db.base import AsyncBaseDb, BaseDb
9
9
  from agno.db.schemas.evals import EvalFilterType, EvalType
10
10
  from agno.models.utils import get_model
11
- from agno.os.auth import get_authentication_dependency
11
+ from agno.os.auth import get_auth_token_from_request, get_authentication_dependency
12
12
  from agno.os.routers.evals.schemas import (
13
13
  DeleteEvalRunsRequest,
14
14
  EvalRunInput,
@@ -33,15 +33,17 @@ from agno.os.schema import (
33
33
  )
34
34
  from agno.os.settings import AgnoAPISettings
35
35
  from agno.os.utils import get_agent_by_id, get_db, get_team_by_id
36
- from agno.team.team import Team
36
+ from agno.remote.base import RemoteDb
37
+ from agno.team import RemoteTeam, Team
38
+ from agno.utils.log import log_warning
37
39
 
38
40
  logger = logging.getLogger(__name__)
39
41
 
40
42
 
41
43
  def get_eval_router(
42
- dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]],
43
- agents: Optional[List[Agent]] = None,
44
- teams: Optional[List[Team]] = None,
44
+ dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]],
45
+ agents: Optional[List[Union[Agent, RemoteAgent]]] = None,
46
+ teams: Optional[List[Union[Team, RemoteTeam]]] = None,
45
47
  settings: AgnoAPISettings = AgnoAPISettings(),
46
48
  ) -> APIRouter:
47
49
  """Create eval router with comprehensive OpenAPI documentation for agent/team evaluation endpoints."""
@@ -61,9 +63,9 @@ def get_eval_router(
61
63
 
62
64
  def attach_routes(
63
65
  router: APIRouter,
64
- dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]],
65
- agents: Optional[List[Agent]] = None,
66
- teams: Optional[List[Team]] = None,
66
+ dbs: dict[str, list[Union[BaseDb, AsyncBaseDb, RemoteDb]]],
67
+ agents: Optional[List[Union[Agent, RemoteAgent]]] = None,
68
+ teams: Optional[List[Union[Team, RemoteTeam]]] = None,
67
69
  ) -> APIRouter:
68
70
  @router.get(
69
71
  "/eval-runs",
@@ -109,6 +111,7 @@ def attach_routes(
109
111
  },
110
112
  )
111
113
  async def get_eval_runs(
114
+ request: Request,
112
115
  agent_id: Optional[str] = Query(default=None, description="Agent ID"),
113
116
  team_id: Optional[str] = Query(default=None, description="Team ID"),
114
117
  workflow_id: Optional[str] = Query(default=None, description="Workflow ID"),
@@ -124,6 +127,23 @@ def attach_routes(
124
127
  ) -> PaginatedResponse[EvalSchema]:
125
128
  db = await get_db(dbs, db_id, table)
126
129
 
130
+ if isinstance(db, RemoteDb):
131
+ auth_token = get_auth_token_from_request(request)
132
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
133
+ return await db.get_eval_runs(
134
+ limit=limit,
135
+ page=page,
136
+ sort_by=sort_by,
137
+ sort_order=sort_order.value if sort_order else None,
138
+ agent_id=agent_id,
139
+ team_id=team_id,
140
+ workflow_id=workflow_id,
141
+ model_id=model_id,
142
+ eval_types=eval_types,
143
+ filter_type=filter_type.value if filter_type else None,
144
+ headers=headers,
145
+ )
146
+
127
147
  # TODO: Delete me:
128
148
  # Filtering out agent-as-judge by default for now,
129
149
  # as they are not supported yet in the AgentOS UI.
@@ -211,11 +231,17 @@ def attach_routes(
211
231
  },
212
232
  )
213
233
  async def get_eval_run(
234
+ request: Request,
214
235
  eval_run_id: str,
215
236
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
216
237
  table: Optional[str] = Query(default=None, description="Table to query eval run from"),
217
238
  ) -> EvalSchema:
218
239
  db = await get_db(dbs, db_id, table)
240
+ if isinstance(db, RemoteDb):
241
+ auth_token = get_auth_token_from_request(request)
242
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
243
+ return await db.get_eval_run(eval_run_id=eval_run_id, db_id=db_id, table=table, headers=headers)
244
+
219
245
  if isinstance(db, AsyncBaseDb):
220
246
  db = cast(AsyncBaseDb, db)
221
247
  eval_run = await db.get_eval_run(eval_run_id=eval_run_id, deserialize=False)
@@ -238,12 +264,20 @@ def attach_routes(
238
264
  },
239
265
  )
240
266
  async def delete_eval_runs(
267
+ http_request: Request,
241
268
  request: DeleteEvalRunsRequest,
242
269
  db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
243
270
  table: Optional[str] = Query(default=None, description="Table to use for deletion"),
244
271
  ) -> None:
245
272
  try:
246
273
  db = await get_db(dbs, db_id, table)
274
+ if isinstance(db, RemoteDb):
275
+ auth_token = get_auth_token_from_request(http_request)
276
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
277
+ return await db.delete_eval_runs(
278
+ eval_run_ids=request.eval_run_ids, db_id=db_id, table=table, headers=headers
279
+ )
280
+
247
281
  if isinstance(db, AsyncBaseDb):
248
282
  db = cast(AsyncBaseDb, db)
249
283
  await db.delete_eval_runs(eval_run_ids=request.eval_run_ids)
@@ -291,6 +325,7 @@ def attach_routes(
291
325
  },
292
326
  )
293
327
  async def update_eval_run(
328
+ http_request: Request,
294
329
  eval_run_id: str,
295
330
  request: UpdateEvalRunRequest,
296
331
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
@@ -298,6 +333,13 @@ def attach_routes(
298
333
  ) -> EvalSchema:
299
334
  try:
300
335
  db = await get_db(dbs, db_id, table)
336
+ if isinstance(db, RemoteDb):
337
+ auth_token = get_auth_token_from_request(http_request)
338
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
339
+ return await db.update_eval_run(
340
+ eval_run_id=eval_run_id, name=request.name, db_id=db_id, table=table, headers=headers
341
+ )
342
+
301
343
  if isinstance(db, AsyncBaseDb):
302
344
  db = cast(AsyncBaseDb, db)
303
345
  eval_run = await db.rename_eval_run(eval_run_id=eval_run_id, name=request.name, deserialize=False)
@@ -352,11 +394,29 @@ def attach_routes(
352
394
  },
353
395
  )
354
396
  async def run_eval(
397
+ request: Request,
355
398
  eval_run_input: EvalRunInput,
356
399
  db_id: Optional[str] = Query(default=None, description="Database ID to use for evaluation"),
357
400
  table: Optional[str] = Query(default=None, description="Table to use for evaluation"),
358
401
  ) -> Optional[EvalSchema]:
359
402
  db = await get_db(dbs, db_id, table)
403
+ if isinstance(db, RemoteDb):
404
+ auth_token = get_auth_token_from_request(request)
405
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
406
+ return await db.create_eval_run(
407
+ eval_type=eval_run_input.eval_type,
408
+ input_text=eval_run_input.input,
409
+ agent_id=eval_run_input.agent_id,
410
+ team_id=eval_run_input.team_id,
411
+ model_id=eval_run_input.model_id,
412
+ model_provider=eval_run_input.model_provider,
413
+ expected_output=eval_run_input.expected_output,
414
+ expected_tool_calls=eval_run_input.expected_tool_calls,
415
+ num_iterations=eval_run_input.num_iterations,
416
+ db_id=db_id,
417
+ table=table,
418
+ headers=headers,
419
+ )
360
420
 
361
421
  if eval_run_input.agent_id and eval_run_input.team_id:
362
422
  raise HTTPException(status_code=400, detail="Only one of agent_id or team_id must be provided")
@@ -365,6 +425,9 @@ def attach_routes(
365
425
  agent = get_agent_by_id(agent_id=eval_run_input.agent_id, agents=agents)
366
426
  if not agent:
367
427
  raise HTTPException(status_code=404, detail=f"Agent with id '{eval_run_input.agent_id}' not found")
428
+ if isinstance(agent, RemoteAgent):
429
+ log_warning("Evaluation against remote agents are not supported yet")
430
+ return None
368
431
 
369
432
  default_model = None
370
433
  if (
@@ -387,6 +450,9 @@ def attach_routes(
387
450
  team = get_team_by_id(team_id=eval_run_input.team_id, teams=teams)
388
451
  if not team:
389
452
  raise HTTPException(status_code=404, detail=f"Team with id '{eval_run_input.team_id}' not found")
453
+ if isinstance(team, RemoteTeam):
454
+ log_warning("Evaluation against remote teams are not supported yet")
455
+ return None
390
456
 
391
457
  # If model_id/model_provider specified, override team's model temporarily
392
458
  default_model = None
@@ -411,21 +477,37 @@ def attach_routes(
411
477
 
412
478
  # Run the evaluation
413
479
  if eval_run_input.eval_type == EvalType.ACCURACY:
480
+ if isinstance(agent, RemoteAgent) or isinstance(team, RemoteTeam):
481
+ # TODO: Handle remote evaluation
482
+ log_warning("Evaluation against remote agents are not supported yet")
483
+ return None
414
484
  return await run_accuracy_eval(
415
485
  eval_run_input=eval_run_input, db=db, agent=agent, team=team, default_model=default_model
416
486
  )
417
487
 
418
488
  elif eval_run_input.eval_type == EvalType.AGENT_AS_JUDGE:
419
489
  return await run_agent_as_judge_eval(
420
- eval_run_input=eval_run_input, db=db, agent=agent, team=team, default_model=default_model
490
+ eval_run_input=eval_run_input,
491
+ db=db,
492
+ agent=agent,
493
+ team=team,
494
+ default_model=default_model, # type: ignore
421
495
  )
422
496
 
423
497
  elif eval_run_input.eval_type == EvalType.PERFORMANCE:
498
+ if isinstance(agent, RemoteAgent) or isinstance(team, RemoteTeam):
499
+ # TODO: Handle remote evaluation
500
+ log_warning("Evaluation against remote agents are not supported yet")
501
+ return None
424
502
  return await run_performance_eval(
425
503
  eval_run_input=eval_run_input, db=db, agent=agent, team=team, default_model=default_model
426
504
  )
427
505
 
428
506
  else:
507
+ if isinstance(agent, RemoteAgent) or isinstance(team, RemoteTeam):
508
+ # TODO: Handle remote evaluation
509
+ log_warning("Evaluation against remote agents are not supported yet")
510
+ return None
429
511
  return await run_reliability_eval(
430
512
  eval_run_input=eval_run_input, db=db, agent=agent, team=team, default_model=default_model
431
513
  )
@@ -2,7 +2,7 @@ from typing import Optional, Union
2
2
 
3
3
  from fastapi import HTTPException
4
4
 
5
- from agno.agent.agent import Agent
5
+ from agno.agent import Agent, RemoteAgent
6
6
  from agno.db.base import AsyncBaseDb, BaseDb
7
7
  from agno.eval.accuracy import AccuracyEval
8
8
  from agno.eval.agent_as_judge import AgentAsJudgeEval
@@ -10,7 +10,7 @@ from agno.eval.performance import PerformanceEval
10
10
  from agno.eval.reliability import ReliabilityEval
11
11
  from agno.models.base import Model
12
12
  from agno.os.routers.evals.schemas import EvalRunInput, EvalSchema
13
- from agno.team.team import Team
13
+ from agno.team import RemoteTeam, Team
14
14
 
15
15
 
16
16
  async def run_accuracy_eval(
@@ -56,8 +56,8 @@ async def run_accuracy_eval(
56
56
  async def run_agent_as_judge_eval(
57
57
  eval_run_input: EvalRunInput,
58
58
  db: Union[BaseDb, AsyncBaseDb],
59
- agent: Optional[Agent] = None,
60
- team: Optional[Team] = None,
59
+ agent: Optional[Union[Agent, RemoteAgent]] = None,
60
+ team: Optional[Union[Team, RemoteTeam]] = None,
61
61
  default_model: Optional[Model] = None,
62
62
  ) -> EvalSchema:
63
63
  """Run an AgentAsJudge evaluation for the given agent or team"""
@@ -109,9 +109,9 @@ async def run_agent_as_judge_eval(
109
109
 
110
110
  # Restore original model after eval
111
111
  if default_model is not None:
112
- if agent is not None:
112
+ if agent is not None and isinstance(agent, Agent):
113
113
  agent.model = default_model
114
- elif team is not None:
114
+ elif team is not None and isinstance(team, Team):
115
115
  team.model = default_model
116
116
 
117
117
  return eval_run
@@ -1,16 +1,16 @@
1
1
  import json
2
2
  import logging
3
3
  import math
4
- from typing import Any, Dict, List, Optional
4
+ from typing import Any, Dict, List, Optional, Union
5
5
 
6
- from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, UploadFile
6
+ from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, Request, UploadFile
7
7
 
8
8
  from agno.knowledge.content import Content, FileData
9
9
  from agno.knowledge.knowledge import Knowledge
10
10
  from agno.knowledge.reader import ReaderFactory
11
11
  from agno.knowledge.reader.base import Reader
12
12
  from agno.knowledge.utils import get_all_chunkers_info, get_all_readers_info, get_content_types_to_readers_mapping
13
- from agno.os.auth import get_authentication_dependency
13
+ from agno.os.auth import get_auth_token_from_request, get_authentication_dependency
14
14
  from agno.os.routers.knowledge.schemas import (
15
15
  ChunkerSchema,
16
16
  ConfigResponseSchema,
@@ -35,6 +35,7 @@ from agno.os.schema import (
35
35
  )
36
36
  from agno.os.settings import AgnoAPISettings
37
37
  from agno.os.utils import get_knowledge_instance_by_db_id
38
+ from agno.remote.base import RemoteKnowledge
38
39
  from agno.utils.log import log_debug, log_info
39
40
  from agno.utils.string import generate_id
40
41
 
@@ -42,7 +43,7 @@ logger = logging.getLogger(__name__)
42
43
 
43
44
 
44
45
  def get_knowledge_router(
45
- knowledge_instances: List[Knowledge], settings: AgnoAPISettings = AgnoAPISettings()
46
+ knowledge_instances: List[Union[Knowledge, RemoteKnowledge]], settings: AgnoAPISettings = AgnoAPISettings()
46
47
  ) -> APIRouter:
47
48
  """Create knowledge router with comprehensive OpenAPI documentation for content management endpoints."""
48
49
  router = APIRouter(
@@ -59,7 +60,7 @@ def get_knowledge_router(
59
60
  return attach_routes(router=router, knowledge_instances=knowledge_instances)
60
61
 
61
62
 
62
- def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> APIRouter:
63
+ def attach_routes(router: APIRouter, knowledge_instances: List[Union[Knowledge, RemoteKnowledge]]) -> APIRouter:
63
64
  @router.post(
64
65
  "/knowledge/content",
65
66
  response_model=ContentResponseSchema,
@@ -93,6 +94,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
93
94
  },
94
95
  )
95
96
  async def upload_content(
97
+ request: Request,
96
98
  background_tasks: BackgroundTasks,
97
99
  name: Optional[str] = Form(None, description="Content name (auto-generated from file/URL if not provided)"),
98
100
  description: Optional[str] = Form(None, description="Content description for context"),
@@ -107,7 +109,6 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
107
109
  db_id: Optional[str] = Query(default=None, description="Database ID to use for content storage"),
108
110
  ):
109
111
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
110
- log_info(f"Adding content: {name}, {description}, {url}, {metadata}")
111
112
 
112
113
  parsed_metadata = None
113
114
  if metadata:
@@ -116,6 +117,25 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
116
117
  except json.JSONDecodeError:
117
118
  # If it's not valid JSON, treat as a simple key-value pair
118
119
  parsed_metadata = {"value": metadata} if metadata != "string" else None
120
+
121
+ if isinstance(knowledge, RemoteKnowledge):
122
+ auth_token = get_auth_token_from_request(request)
123
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
124
+ return await knowledge.upload_content(
125
+ name=name,
126
+ description=description,
127
+ url=url,
128
+ metadata=parsed_metadata,
129
+ file=file,
130
+ text_content=text_content,
131
+ reader_id=reader_id,
132
+ chunker=chunker,
133
+ chunk_size=chunk_size,
134
+ chunk_overlap=chunk_overlap,
135
+ db_id=db_id,
136
+ headers=headers,
137
+ )
138
+
119
139
  if file:
120
140
  content_bytes = await file.read()
121
141
  elif text_content:
@@ -225,6 +245,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
225
245
  },
226
246
  )
227
247
  async def update_content(
248
+ request: Request,
228
249
  content_id: str = Path(..., description="Content ID"),
229
250
  name: Optional[str] = Form(None, description="Content name"),
230
251
  description: Optional[str] = Form(None, description="Content description"),
@@ -242,6 +263,19 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
242
263
  except json.JSONDecodeError:
243
264
  raise HTTPException(status_code=400, detail="Invalid JSON format for metadata")
244
265
 
266
+ if isinstance(knowledge, RemoteKnowledge):
267
+ auth_token = get_auth_token_from_request(request)
268
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
269
+ return await knowledge.update_content(
270
+ content_id=content_id,
271
+ name=name,
272
+ description=description,
273
+ metadata=parsed_metadata,
274
+ reader_id=reader_id,
275
+ db_id=db_id,
276
+ headers=headers,
277
+ )
278
+
245
279
  # Create ContentUpdateSchema object from form data
246
280
  update_data = ContentUpdateSchema(
247
281
  name=name if name and name.strip() else None,
@@ -309,6 +343,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
309
343
  },
310
344
  )
311
345
  async def get_content(
346
+ request: Request,
312
347
  limit: Optional[int] = Query(default=20, description="Number of content entries to return"),
313
348
  page: Optional[int] = Query(default=1, description="Page number"),
314
349
  sort_by: Optional[str] = Query(default="created_at", description="Field to sort by"),
@@ -316,6 +351,19 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
316
351
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
317
352
  ) -> PaginatedResponse[ContentResponseSchema]:
318
353
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
354
+
355
+ if isinstance(knowledge, RemoteKnowledge):
356
+ auth_token = get_auth_token_from_request(request)
357
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
358
+ return await knowledge.get_content(
359
+ limit=limit,
360
+ page=page,
361
+ sort_by=sort_by,
362
+ sort_order=sort_order.value if sort_order else None,
363
+ db_id=db_id,
364
+ headers=headers,
365
+ )
366
+
319
367
  contents, count = await knowledge.aget_content(limit=limit, page=page, sort_by=sort_by, sort_order=sort_order)
320
368
 
321
369
  return PaginatedResponse(
@@ -377,11 +425,16 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
377
425
  },
378
426
  )
379
427
  async def get_content_by_id(
428
+ request: Request,
380
429
  content_id: str,
381
430
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
382
431
  ) -> ContentResponseSchema:
383
- log_info(f"Getting content by id: {content_id}")
384
432
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
433
+ if isinstance(knowledge, RemoteKnowledge):
434
+ auth_token = get_auth_token_from_request(request)
435
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
436
+ return await knowledge.get_content_by_id(content_id=content_id, db_id=db_id, headers=headers)
437
+
385
438
  content = await knowledge.aget_content_by_id(content_id=content_id)
386
439
  if not content:
387
440
  raise HTTPException(status_code=404, detail=f"Content not found: {content_id}")
@@ -417,12 +470,17 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
417
470
  },
418
471
  )
419
472
  async def delete_content_by_id(
473
+ request: Request,
420
474
  content_id: str,
421
475
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
422
476
  ) -> ContentResponseSchema:
423
477
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
424
- await knowledge.aremove_content_by_id(content_id=content_id)
425
- log_info(f"Deleting content by id: {content_id}")
478
+ if isinstance(knowledge, RemoteKnowledge):
479
+ auth_token = get_auth_token_from_request(request)
480
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
481
+ await knowledge.delete_content_by_id(content_id=content_id, db_id=db_id, headers=headers)
482
+ else:
483
+ await knowledge.aremove_content_by_id(content_id=content_id)
426
484
 
427
485
  return ContentResponseSchema(
428
486
  id=content_id,
@@ -442,12 +500,17 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
442
500
  500: {"description": "Failed to delete all content", "model": InternalServerErrorResponse},
443
501
  },
444
502
  )
445
- def delete_all_content(
503
+ async def delete_all_content(
504
+ request: Request,
446
505
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
447
506
  ):
448
507
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
449
- log_info("Deleting all content")
450
- knowledge.remove_all_content()
508
+ if isinstance(knowledge, RemoteKnowledge):
509
+ auth_token = get_auth_token_from_request(request)
510
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
511
+ return await knowledge.delete_all_content(db_id=db_id, headers=headers)
512
+
513
+ await knowledge.aremove_all_content()
451
514
  return "success"
452
515
 
453
516
  @router.get(
@@ -481,11 +544,16 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
481
544
  },
482
545
  )
483
546
  async def get_content_status(
547
+ request: Request,
484
548
  content_id: str,
485
549
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
486
550
  ) -> ContentStatusResponse:
487
- log_info(f"Getting content status: {content_id}")
488
551
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
552
+ if isinstance(knowledge, RemoteKnowledge):
553
+ auth_token = get_auth_token_from_request(request)
554
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
555
+ return await knowledge.get_content_status(content_id=content_id, db_id=db_id, headers=headers)
556
+
489
557
  knowledge_status, status_message = await knowledge.aget_content_status(content_id=content_id)
490
558
 
491
559
  # Handle the case where content is not found
@@ -550,13 +618,27 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
550
618
  404: {"description": "No documents found"},
551
619
  },
552
620
  )
553
- def search_knowledge(request: VectorSearchRequestSchema) -> PaginatedResponse[VectorSearchResult]:
621
+ async def search_knowledge(
622
+ http_request: Request, request: VectorSearchRequestSchema
623
+ ) -> PaginatedResponse[VectorSearchResult]:
554
624
  import time
555
625
 
556
626
  start_time = time.time()
557
627
 
558
628
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, request.db_id)
559
629
 
630
+ if isinstance(knowledge, RemoteKnowledge):
631
+ auth_token = get_auth_token_from_request(http_request)
632
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
633
+ return await knowledge.search_knowledge(
634
+ query=request.query,
635
+ max_results=request.max_results,
636
+ filters=request.filters,
637
+ search_type=request.search_type,
638
+ db_id=request.db_id,
639
+ headers=headers,
640
+ )
641
+
560
642
  # For now, validate the vector db ids exist in the knowledge base
561
643
  # We will add more logic around this once we have multi vectordb support
562
644
  # If vector db ids are provided, check if any of them match the knowledge's vector db
@@ -578,7 +660,7 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
578
660
  # Use max_results if specified, otherwise use a higher limit for search then paginate
579
661
  search_limit = request.max_results
580
662
 
581
- results = knowledge.search(
663
+ results = await knowledge.async_search(
582
664
  query=request.query, max_results=search_limit, filters=request.filters, search_type=request.search_type
583
665
  )
584
666
 
@@ -869,11 +951,17 @@ def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> AP
869
951
  }
870
952
  },
871
953
  )
872
- def get_config(
954
+ async def get_config(
955
+ request: Request,
873
956
  db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
874
957
  ) -> ConfigResponseSchema:
875
958
  knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
876
959
 
960
+ if isinstance(knowledge, RemoteKnowledge):
961
+ auth_token = get_auth_token_from_request(request)
962
+ headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else None
963
+ return await knowledge.get_config(headers=headers)
964
+
877
965
  # Get factory readers info (including custom readers from this knowledge instance)
878
966
  readers_info = get_all_readers_info(knowledge)
879
967
  reader_schemas = {}