agent-api-server 2.1.7__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 (52) hide show
  1. agent_api_server/__init__.py +0 -0
  2. agent_api_server/api/__init__.py +0 -0
  3. agent_api_server/api/v1/__init__.py +0 -0
  4. agent_api_server/api/v1/api.py +25 -0
  5. agent_api_server/api/v1/config.py +57 -0
  6. agent_api_server/api/v1/graph.py +59 -0
  7. agent_api_server/api/v1/schema.py +57 -0
  8. agent_api_server/api/v1/thread.py +563 -0
  9. agent_api_server/cache/__init__.py +0 -0
  10. agent_api_server/cache/redis_cache.py +385 -0
  11. agent_api_server/callback_handler.py +18 -0
  12. agent_api_server/client/css/styles.css +1202 -0
  13. agent_api_server/client/favicon.ico +0 -0
  14. agent_api_server/client/index.html +102 -0
  15. agent_api_server/client/js/app.js +1499 -0
  16. agent_api_server/client/js/index.umd.js +824 -0
  17. agent_api_server/config_center/config_center.py +239 -0
  18. agent_api_server/configs/__init__.py +3 -0
  19. agent_api_server/configs/config.py +163 -0
  20. agent_api_server/dynamic_llm/__init__.py +0 -0
  21. agent_api_server/dynamic_llm/dynamic_llm.py +331 -0
  22. agent_api_server/listener.py +530 -0
  23. agent_api_server/log/__init__.py +0 -0
  24. agent_api_server/log/formatters.py +122 -0
  25. agent_api_server/log/logging.json +50 -0
  26. agent_api_server/mcp_convert/__init__.py +0 -0
  27. agent_api_server/mcp_convert/mcp_convert.py +375 -0
  28. agent_api_server/memeory/__init__.py +0 -0
  29. agent_api_server/memeory/postgres.py +233 -0
  30. agent_api_server/register/__init__.py +0 -0
  31. agent_api_server/register/register.py +65 -0
  32. agent_api_server/service.py +354 -0
  33. agent_api_server/service_hub/service_hub.py +233 -0
  34. agent_api_server/service_hub/service_hub_test.py +700 -0
  35. agent_api_server/shared/__init__.py +0 -0
  36. agent_api_server/shared/ase.py +54 -0
  37. agent_api_server/shared/base_model.py +103 -0
  38. agent_api_server/shared/common.py +110 -0
  39. agent_api_server/shared/decode_token.py +107 -0
  40. agent_api_server/shared/detect_message.py +410 -0
  41. agent_api_server/shared/get_model_info.py +491 -0
  42. agent_api_server/shared/message.py +419 -0
  43. agent_api_server/shared/util_func.py +372 -0
  44. agent_api_server/sso_service/__init__.py +1 -0
  45. agent_api_server/sso_service/sdk/__init__.py +1 -0
  46. agent_api_server/sso_service/sdk/client.py +224 -0
  47. agent_api_server/sso_service/sdk/credential.py +11 -0
  48. agent_api_server/sso_service/sdk/encoding.py +22 -0
  49. agent_api_server/sso_service/sso_service.py +177 -0
  50. agent_api_server-2.1.7.dist-info/METADATA +130 -0
  51. agent_api_server-2.1.7.dist-info/RECORD +52 -0
  52. agent_api_server-2.1.7.dist-info/WHEEL +4 -0
File without changes
@@ -0,0 +1,54 @@
1
+ import base64
2
+ import hashlib
3
+ import os
4
+
5
+ from cryptography.hazmat.backends import default_backend
6
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
7
+
8
+
9
+ class AESCipher:
10
+ def __init__(self, key_string):
11
+ self.key = hashlib.sha256(key_string.encode()).digest()
12
+ self.backend = default_backend()
13
+
14
+ def encrypt(self, plaintext):
15
+ iv = os.urandom(16)
16
+ plaintext_bytes = plaintext.encode("utf-8")
17
+
18
+ encryptor = Cipher(algorithms.AES(self.key), modes.CBC(iv), backend=self.backend).encryptor()
19
+ padder = self._create_padder()
20
+
21
+ padded_plaintext = padder.update(plaintext_bytes) + padder.finalize()
22
+ ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize()
23
+ encrypted_data = base64.b64encode(iv + ciphertext).decode("utf-8")
24
+
25
+ return encrypted_data
26
+
27
+ def decrypt(self, encrypted_data):
28
+ try:
29
+ encrypted_bytes = base64.b64decode(encrypted_data)
30
+
31
+ iv = encrypted_bytes[:16]
32
+ ciphertext = encrypted_bytes[16:]
33
+
34
+ decryptor = Cipher(algorithms.AES(self.key), modes.CBC(iv), backend=self.backend).decryptor()
35
+ padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()
36
+
37
+ unpadder = self._create_unpadder()
38
+ plaintext_bytes = unpadder.update(padded_plaintext) + unpadder.finalize()
39
+
40
+ plaintext = plaintext_bytes.decode("utf-8")
41
+
42
+ return plaintext
43
+ except Exception as e:
44
+ raise ValueError(f"Decryption failed: {str(e)}")
45
+
46
+ @staticmethod
47
+ def _create_padder():
48
+ from cryptography.hazmat.primitives.padding import PKCS7
49
+ return PKCS7(algorithms.AES.block_size).padder()
50
+
51
+ @staticmethod
52
+ def _create_unpadder():
53
+ from cryptography.hazmat.primitives.padding import PKCS7
54
+ return PKCS7(algorithms.AES.block_size).unpadder()
@@ -0,0 +1,103 @@
1
+ import logging
2
+ from typing import Optional
3
+ from pydantic import Field
4
+ from pydantic import BaseModel
5
+ from fastapi import HTTPException, status
6
+ from typing import Any, Literal, NotRequired
7
+ from typing_extensions import TypedDict
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ class ThreadInfo(BaseModel):
12
+ thread_id: str
13
+ graph_name: str
14
+ status: str
15
+
16
+ class RunResponse(BaseModel):
17
+ status: str
18
+ thread_id: str
19
+ result: Optional[Any] = None
20
+ error: Optional[str] = None
21
+
22
+ def error_response(
23
+ status_code: int,
24
+ error_type: str,
25
+ message: str,
26
+ **additional_info: Any
27
+ ) -> HTTPException:
28
+ """Helper function to create consistent error responses."""
29
+ detail = {
30
+ "status": "error",
31
+ "error": error_type,
32
+ "message": message,
33
+ **additional_info
34
+ }
35
+ return HTTPException(
36
+ status_code=status_code,
37
+ detail=detail
38
+ )
39
+
40
+ class ToolCall(TypedDict):
41
+ """Represents a request to call a tool."""
42
+
43
+ name: str
44
+ """The name of the tool to be called."""
45
+ args: dict[str, Any]
46
+ """The arguments to the tool call."""
47
+ id: str | None
48
+ """An identifier associated with the tool call."""
49
+ type: NotRequired[Literal["tool_call"]]
50
+
51
+ class ChatMessage(BaseModel):
52
+ """Message in a chat."""
53
+ type: Literal["human", "ai", "tool"] = Field(
54
+ description="Role of the message.",
55
+ examples=["human", "ai", "tool"],
56
+ )
57
+ content_type: Literal["markdown", "json", "html", "xml", "python", "yaml", "text"] = Field(
58
+ description="The data type of the message.",
59
+ examples=["markdown", "json", "html", "xml", "python", "yaml", "text"],
60
+ )
61
+ content: str = Field(
62
+ description="Content of the message.",
63
+ examples=["Hello, world!"],
64
+ )
65
+ tool_calls: list[ToolCall] = Field(
66
+ description="Tool calls in the message.",
67
+ default=[],
68
+ )
69
+ response_metadata: dict[str, Any] = Field(
70
+ description="Response metadata. For example: response headers, logprobs, token counts.",
71
+ default={},
72
+ )
73
+ references: list[dict[str, Any]] = Field(
74
+ description="references metadata. For example: List of reference materials.",
75
+ default=[],
76
+ )
77
+
78
+ def pretty_repr(self) -> str:
79
+ """Get a pretty representation of the message."""
80
+ base_title = self.type.title() + " Message"
81
+ padded = " " + base_title + " "
82
+ sep_len = (80 - len(padded)) // 2
83
+ sep = "=" * sep_len
84
+ second_sep = sep + "=" if len(padded) % 2 else sep
85
+ title = f"{sep}{padded}{second_sep}"
86
+ return f"{title}\n\n{self.content}"
87
+
88
+ def pretty_print(self) -> None:
89
+ logger.info(self.pretty_repr()) # noqa: T201
90
+
91
+
92
+ def sse_response_example() -> dict[int | str, Any]:
93
+ return {
94
+ status.HTTP_200_OK: {
95
+ "description": "Server Sent Event Response",
96
+ "content": {
97
+ "text/event-stream": {
98
+ "example": "data: {'type': 'token', 'content': 'Hello'}\n\ndata: {'type': 'token', 'content': ' World'}\n\ndata: [DONE]\n\n",
99
+ "schema": {"type": "string"},
100
+ }
101
+ },
102
+ }
103
+ }
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+ from typing import Dict, Set
3
+ from enum import Enum
4
+
5
+
6
+ class ConfigCategory(Enum):
7
+ CHAT_PROVIDER = "CHAT_PROVIDER"
8
+ CHAT_MODEL = "CHAT_MODEL"
9
+ CHAT_CREDENTIALS = "CHAT_CREDENTIALS"
10
+
11
+ EMBEDDING_PROVIDER = "EMBEDDING_PROVIDER"
12
+ EMBEDDING_MODEL = "EMBEDDING_MODEL"
13
+ EMBEDDING_CREDENTIALS = "EMBEDDING_CREDENTIALS"
14
+
15
+ RERANK_PROVIDER = "RERANK_PROVIDER"
16
+ RERANK_MODEL = "RERANK_MODEL"
17
+ RERANK_CREDENTIALS = "RERANK_CREDENTIALS"
18
+
19
+ AGENT_ID = "AGENT_ID"
20
+
21
+ MODEL_SUFFIX_MAP = {
22
+ "CHAT_PROVIDER": "llm",
23
+ "EMBEDDING_PROVIDER": "text-embedding",
24
+ "RERANK_PROVIDER": "rerank",
25
+ }
26
+
27
+ PROVIDER_FIELDS = {
28
+ ConfigCategory.CHAT_PROVIDER.value: "llm",
29
+ ConfigCategory.EMBEDDING_PROVIDER.value: "text-embedding",
30
+ ConfigCategory.RERANK_PROVIDER.value: "rerank"
31
+ }
32
+
33
+ def process_model_from_config_dict(input_dict: Dict) -> Dict:
34
+ result = {
35
+ "models": {},
36
+ "required": {}
37
+ }
38
+
39
+ if input_dict is None:
40
+ return result
41
+
42
+ properties = input_dict.get('properties', {})
43
+
44
+ tool_model_map: Dict[str, Set[str]] = {}
45
+
46
+ for prop_name, prop_def in properties.items():
47
+ prop_name_upper = prop_name.upper()
48
+
49
+ if prop_name_upper in PROVIDER_FIELDS:
50
+ tool_name = "default"
51
+ model_type = PROVIDER_FIELDS[prop_name_upper]
52
+ if tool_name not in tool_model_map:
53
+ tool_model_map[tool_name] = set()
54
+ tool_model_map[tool_name].add(model_type)
55
+ continue
56
+
57
+ if prop_name_upper.endswith("_CHAT_PROVIDER"):
58
+ tool_name = prop_name_upper[:-len("_CHAT_PROVIDER")].lower()
59
+ if not tool_name:
60
+ tool_name = "default"
61
+ model_type = "llm"
62
+ elif prop_name_upper.endswith("_EMBEDDING_PROVIDER"):
63
+ tool_name = prop_name_upper[:-len("_EMBEDDING_PROVIDER")].lower()
64
+ if not tool_name:
65
+ tool_name = "default"
66
+ model_type = "text-embedding"
67
+ elif prop_name_upper.endswith("_RERANK_PROVIDER"):
68
+ tool_name = prop_name_upper[:-len("_RERANK_PROVIDER")].lower()
69
+ if not tool_name:
70
+ tool_name = "default"
71
+ model_type = "rerank"
72
+ else:
73
+ continue
74
+
75
+ if tool_name:
76
+ if tool_name not in tool_model_map:
77
+ tool_model_map[tool_name] = set()
78
+ tool_model_map[tool_name].add(model_type)
79
+
80
+ standard_fields = ['CHAT_PROVIDER', 'EMBEDDING_PROVIDER']
81
+ for field in standard_fields:
82
+ if field in properties:
83
+ tool_name = "default"
84
+ model_type = "llm" if field == "CHAT_PROVIDER" else "text-embedding"
85
+ if tool_name not in tool_model_map:
86
+ tool_model_map[tool_name] = set()
87
+ tool_model_map[tool_name].add(model_type)
88
+
89
+ if not tool_model_map:
90
+ for prop_name in properties.keys():
91
+ prop_name_upper = prop_name.upper()
92
+ if 'CHAT' in prop_name_upper and 'PROVIDER' in prop_name_upper:
93
+ tool_name = "default"
94
+ model_type = "llm"
95
+ if tool_name not in tool_model_map:
96
+ tool_model_map[tool_name] = set()
97
+ tool_model_map[tool_name].add(model_type)
98
+ elif 'EMBEDDING' in prop_name_upper and 'PROVIDER' in prop_name_upper:
99
+ tool_name = "default"
100
+ model_type = "text-embedding"
101
+ if tool_name not in tool_model_map:
102
+ tool_model_map[tool_name] = set()
103
+ tool_model_map[tool_name].add(model_type)
104
+
105
+ for tool, model_types in tool_model_map.items():
106
+ model_list = list(model_types)
107
+ result['models'][tool] = model_list
108
+ result['required'][tool] = model_list.copy()
109
+
110
+ return result
@@ -0,0 +1,107 @@
1
+ import base64
2
+ import json
3
+ import logging
4
+ from datetime import datetime
5
+ from typing import Dict, Any, Optional, Tuple
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def decode_jwt(token: str) -> Tuple[Optional[Dict], Optional[Dict], Optional[Dict]]:
11
+ try:
12
+ parts = token.split('.')
13
+
14
+ if len(parts) != 3:
15
+ logger.error("Invalid JWT format: expected 3 parts, got %d", len(parts))
16
+ return None, None, None
17
+
18
+ header = _decode_base64_json(parts[0], "header")
19
+ if not header:
20
+ return None, None, None
21
+
22
+ payload = _decode_base64_json(parts[1], "payload")
23
+ if not payload:
24
+ return None, None, None
25
+
26
+ user_info = _extract_user_info(payload)
27
+
28
+ _log_jwt_info(header, payload, parts[2])
29
+
30
+ return header, payload, user_info
31
+
32
+ except Exception as e:
33
+ logger.error("Failed to decode JWT: %s", str(e))
34
+ return None, None, None
35
+
36
+
37
+ def _decode_base64_json(encoded_str: str, component_name: str) -> Optional[Dict]:
38
+ """Decode base64 URL safe string to JSON dictionary."""
39
+ try:
40
+ # Pad with '=' for proper base64 decoding
41
+ padding_needed = len(encoded_str) % 4
42
+ if padding_needed:
43
+ encoded_str += '=' * (4 - padding_needed)
44
+
45
+ decoded_bytes = base64.urlsafe_b64decode(encoded_str)
46
+ decoded_dict = json.loads(decoded_bytes)
47
+ return decoded_dict
48
+
49
+ except Exception as e:
50
+ logger.error("Failed to decode %s: %s", component_name, str(e))
51
+ return None
52
+
53
+
54
+ def _extract_user_info(payload: Dict) -> Dict[str, Any]:
55
+ user_info = {}
56
+
57
+ user_id = payload.get('id')
58
+ if user_id:
59
+ user_info['id'] = user_id
60
+ logger.info("Extracted user ID: %s", user_id)
61
+ else:
62
+ logger.warning("User ID not found in payload")
63
+
64
+ username = payload.get('username')
65
+ if username:
66
+ user_info['username'] = username
67
+ logger.info("Extracted username: %s", username)
68
+ else:
69
+ logger.warning("Username not found in payload")
70
+
71
+ return user_info
72
+
73
+
74
+ def _log_jwt_info(header: Dict, payload: Dict, signature: str) -> None:
75
+ logger.info("JWT Decoded Successfully")
76
+ logger.info("=" * 50)
77
+
78
+ logger.info("Header:")
79
+ logger.info(json.dumps(header, indent=2))
80
+ logger.info("")
81
+
82
+ logger.info("Payload:")
83
+ for key, value in payload.items():
84
+ if key in ['creationTime', 'exp', 'iat', 'lastModifiedTime'] and isinstance(value, (int, float)):
85
+ try:
86
+ dt = datetime.fromtimestamp(value)
87
+ logger.info(" %s: %s (%s)", key, value, dt.strftime('%Y-%m-%d %H:%M:%S'))
88
+ except Exception:
89
+ logger.info(" %s: %s", key, value)
90
+ else:
91
+ logger.info(" %s: %s", key, value)
92
+
93
+ signature_preview = signature[:50] + "..." if len(signature) > 50 else signature
94
+ logger.info("\nSignature: %s", signature_preview)
95
+ logger.info("=" * 50)
96
+
97
+
98
+ if __name__ == "__main__":
99
+ # Example token (truncated for demonstration)
100
+ example_token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJjb3VudHJ5IjoiQ04iLCJjcmVhdGlvblRpbWUiOjE3NjQxNDgwNTMsImV4cCI6MTc3MjI2MDYxNywiZmlyc3ROYW1lIjoiYWRtaW4iLCJpYXQiOjE3NzIyNTcwMTcsImlkIjoiZTQ2MDk2YWUtODhmMC00NDIxLWI1YmUtMTU3OTAxZmRhZDZkIiwiaXNzIjoid2lzZS1wYWFzIiwibGFzdE1vZGlmaWVkVGltZSI6MTc3MjE2MDg3OCwibGFzdE5hbWUiOiJhZG1pbiIsImxvZ2luIjoiYWRtaW5AaW90LnNlbnNlIiwicmVmcmVzaFRva2VuIjoiN2Y4ZTkzNmMtMTQ2Ny0xMWYxLThiODUtZTJlZTMyNTQ2MWJlIiwic3RhdHVzIjoiQWN0aXZlIiwidXNlcm5hbWUiOiJhZG1pbkBpb3Quc2Vuc2UifQ.cmxJ4OG6xsKKK0qom2ur84qmra0P1_nGmCvEM7YRThN8ECyJNXQaLnXYhzEHYjbtr9anBuUoMwqkIc1kZg7t2Q"
101
+
102
+ header, payload, user_info = decode_jwt(example_token)
103
+
104
+ if user_info:
105
+ print(f"\nExtracted User Information:")
106
+ print(f"User ID: {user_info.get('id', 'Not found')}")
107
+ print(f"Username: {user_info.get('username', 'Not found')}")