pycityagent 2.0.0a65__cp312-cp312-macosx_11_0_arm64.whl → 2.0.0a67__cp312-cp312-macosx_11_0_arm64.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 (87) hide show
  1. pycityagent/agent/agent.py +157 -57
  2. pycityagent/agent/agent_base.py +316 -43
  3. pycityagent/cityagent/bankagent.py +49 -9
  4. pycityagent/cityagent/blocks/__init__.py +1 -2
  5. pycityagent/cityagent/blocks/cognition_block.py +54 -31
  6. pycityagent/cityagent/blocks/dispatcher.py +22 -17
  7. pycityagent/cityagent/blocks/economy_block.py +46 -32
  8. pycityagent/cityagent/blocks/mobility_block.py +209 -105
  9. pycityagent/cityagent/blocks/needs_block.py +101 -54
  10. pycityagent/cityagent/blocks/other_block.py +42 -33
  11. pycityagent/cityagent/blocks/plan_block.py +59 -42
  12. pycityagent/cityagent/blocks/social_block.py +167 -126
  13. pycityagent/cityagent/blocks/utils.py +13 -6
  14. pycityagent/cityagent/firmagent.py +17 -35
  15. pycityagent/cityagent/governmentagent.py +3 -3
  16. pycityagent/cityagent/initial.py +79 -49
  17. pycityagent/cityagent/memory_config.py +123 -94
  18. pycityagent/cityagent/message_intercept.py +0 -4
  19. pycityagent/cityagent/metrics.py +41 -0
  20. pycityagent/cityagent/nbsagent.py +24 -36
  21. pycityagent/cityagent/societyagent.py +9 -4
  22. pycityagent/cli/wrapper.py +2 -2
  23. pycityagent/economy/econ_client.py +407 -81
  24. pycityagent/environment/__init__.py +0 -3
  25. pycityagent/environment/sim/__init__.py +0 -3
  26. pycityagent/environment/sim/aoi_service.py +2 -2
  27. pycityagent/environment/sim/client.py +3 -31
  28. pycityagent/environment/sim/clock_service.py +2 -2
  29. pycityagent/environment/sim/lane_service.py +8 -8
  30. pycityagent/environment/sim/light_service.py +8 -8
  31. pycityagent/environment/sim/pause_service.py +9 -10
  32. pycityagent/environment/sim/person_service.py +20 -20
  33. pycityagent/environment/sim/road_service.py +2 -2
  34. pycityagent/environment/sim/sim_env.py +21 -5
  35. pycityagent/environment/sim/social_service.py +4 -4
  36. pycityagent/environment/simulator.py +249 -27
  37. pycityagent/environment/utils/__init__.py +2 -2
  38. pycityagent/environment/utils/geojson.py +2 -2
  39. pycityagent/environment/utils/grpc.py +4 -4
  40. pycityagent/environment/utils/map_utils.py +2 -2
  41. pycityagent/llm/embeddings.py +147 -28
  42. pycityagent/llm/llm.py +178 -111
  43. pycityagent/llm/llmconfig.py +5 -0
  44. pycityagent/llm/utils.py +4 -0
  45. pycityagent/memory/__init__.py +0 -4
  46. pycityagent/memory/const.py +2 -2
  47. pycityagent/memory/faiss_query.py +140 -61
  48. pycityagent/memory/memory.py +394 -91
  49. pycityagent/memory/memory_base.py +140 -34
  50. pycityagent/memory/profile.py +13 -13
  51. pycityagent/memory/self_define.py +13 -13
  52. pycityagent/memory/state.py +14 -14
  53. pycityagent/message/message_interceptor.py +253 -3
  54. pycityagent/message/messager.py +133 -6
  55. pycityagent/metrics/mlflow_client.py +47 -4
  56. pycityagent/pycityagent-sim +0 -0
  57. pycityagent/pycityagent-ui +0 -0
  58. pycityagent/simulation/__init__.py +3 -2
  59. pycityagent/simulation/agentgroup.py +150 -54
  60. pycityagent/simulation/simulation.py +276 -66
  61. pycityagent/survey/manager.py +45 -3
  62. pycityagent/survey/models.py +42 -2
  63. pycityagent/tools/__init__.py +1 -2
  64. pycityagent/tools/tool.py +93 -69
  65. pycityagent/utils/avro_schema.py +2 -2
  66. pycityagent/utils/parsers/code_block_parser.py +1 -1
  67. pycityagent/utils/parsers/json_parser.py +2 -2
  68. pycityagent/utils/parsers/parser_base.py +2 -2
  69. pycityagent/workflow/block.py +64 -13
  70. pycityagent/workflow/prompt.py +31 -23
  71. pycityagent/workflow/trigger.py +91 -24
  72. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/METADATA +2 -2
  73. pycityagent-2.0.0a67.dist-info/RECORD +97 -0
  74. pycityagent/environment/interact/__init__.py +0 -0
  75. pycityagent/environment/interact/interact.py +0 -198
  76. pycityagent/environment/message/__init__.py +0 -0
  77. pycityagent/environment/sence/__init__.py +0 -0
  78. pycityagent/environment/sence/static.py +0 -416
  79. pycityagent/environment/sidecar/__init__.py +0 -8
  80. pycityagent/environment/sidecar/sidecarv2.py +0 -109
  81. pycityagent/environment/sim/economy_services.py +0 -192
  82. pycityagent/metrics/utils/const.py +0 -0
  83. pycityagent-2.0.0a65.dist-info/RECORD +0 -105
  84. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/LICENSE +0 -0
  85. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/WHEEL +0 -0
  86. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/entry_points.txt +0 -0
  87. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/top_level.txt +0 -0
@@ -15,6 +15,14 @@ __all__ = [
15
15
 
16
16
 
17
17
  class SentenceEmbedding(Embeddings):
18
+ """
19
+ Main class for generating sentence embeddings using a pre-trained language model.
20
+
21
+ - **Description**:
22
+ - This class initializes a tokenizer and a pre-trained model to generate embeddings for input texts.
23
+ - It supports automatic CUDA device allocation and handles caching of models locally.
24
+ """
25
+
18
26
  def __init__(
19
27
  self,
20
28
  pretrained_model_name_or_path: Union[str, os.PathLike] = "BAAI/bge-m3",
@@ -24,6 +32,17 @@ class SentenceEmbedding(Embeddings):
24
32
  cache_dir: str = "./cache",
25
33
  proxies: Optional[dict] = None,
26
34
  ):
35
+ """
36
+ Initializes the SentenceEmbedding instance.
37
+
38
+ - **Parameters**:
39
+ - `pretrained_model_name_or_path`: Name or path of the pre-trained model. Default is "BAAI/bge-m3".
40
+ - `max_seq_len`: Maximum sequence length for inputs. Default is 8192.
41
+ - `auto_cuda`: Automatically move the model to available CUDA device if True. Default is False.
42
+ - `local_files_only`: Only try to load model from local files if True. Default is False.
43
+ - `cache_dir`: Directory to cache models. Default is "./cache".
44
+ - `proxies`: Proxy settings for HTTP/HTTPS. Default is None.
45
+ """
27
46
  os.makedirs(cache_dir, exist_ok=True)
28
47
  self.tokenizer = AutoTokenizer.from_pretrained(
29
48
  pretrained_model_name_or_path,
@@ -46,6 +65,15 @@ class SentenceEmbedding(Embeddings):
46
65
  self.max_seq_len = max_seq_len
47
66
 
48
67
  def _embed(self, texts: list[str]) -> list[list[float]]:
68
+ """
69
+ Internal method to compute embeddings for a list of input texts.
70
+
71
+ - **Parameters**:
72
+ - `texts`: A list of strings representing the input texts.
73
+
74
+ - **Returns**:
75
+ - A list of lists containing floating-point numbers representing the embeddings.
76
+ """
49
77
  # Tokenize sentences
50
78
  encoded_input = self.tokenizer(
51
79
  texts, padding=True, truncation=True, return_tensors="pt"
@@ -73,27 +101,46 @@ class SentenceEmbedding(Embeddings):
73
101
  return sentence_embeddings.tolist()
74
102
 
75
103
  def embed_documents(self, texts: list[str]) -> list[list[float]]:
76
- """Embed documents."""
104
+ """
105
+ Embeds a list of documents.
106
+
107
+ - **Parameters**:
108
+ - `texts`: The documents to embed.
109
+
110
+ - **Returns**:
111
+ - A list of document embeddings.
112
+ """
77
113
  return self._embed(texts)
78
114
 
79
115
  def embed_query(self, text: str) -> list[float]:
80
- """Embed query text."""
116
+ """
117
+ Embeds a single query text.
118
+
119
+ - **Parameters**:
120
+ - `text`: The query text to embed.
121
+
122
+ - **Returns**:
123
+ - The embedding of the query text.
124
+ """
81
125
  return self._embed([text])[0]
82
126
 
83
127
 
84
128
  class SimpleEmbedding(Embeddings):
85
- """简单的基于内存的embedding实现
129
+ """
130
+ A simple in-memory embedding implementation using Bag of Words and TF-IDF for generating text vectors.
86
131
 
87
- 使用简单的词袋模型(Bag of Words)和TF-IDF来生成文本的向量表示。
88
- 所有向量都保存在内存中,适用于小规模应用。
132
+ - **Description**:
133
+ - This class provides a basic approach to creating text embeddings based on term frequency-inverse document frequency (TF-IDF).
134
+ - It maintains an in-memory cache of computed embeddings and updates its vocabulary dynamically as new texts are processed.
89
135
  """
90
136
 
91
137
  def __init__(self, vector_dim: int = 128, cache_size: int = 1000):
92
- """初始化
138
+ """
139
+ Initializes the SimpleEmbedding instance.
93
140
 
94
- Args:
95
- vector_dim: 向量维度
96
- cache_size: 缓存大小,超过此大小将清除最早的缓存
141
+ - **Parameters**:
142
+ - `vector_dim`: Dimensionality of the vectors. Default is 128.
143
+ - `cache_size`: Cache size; oldest entries are removed when exceeded. Default is 1000.
97
144
  """
98
145
  self.vector_dim = vector_dim
99
146
  self.cache_size = cache_size
@@ -103,29 +150,73 @@ class SimpleEmbedding(Embeddings):
103
150
  self._doc_count = 0 # 文档总数
104
151
 
105
152
  def _text_to_hash(self, text: str) -> str:
106
- """将文本转换为hash值"""
153
+ """
154
+ Converts text into a hash value.
155
+
156
+ - **Parameters**:
157
+ - `text`: The input text to hash.
158
+
159
+ - **Returns**:
160
+ - A string representing the MD5 hash of the input text.
161
+ """
107
162
  return hashlib.md5(text.encode()).hexdigest()
108
163
 
109
164
  def _tokenize(self, text: str) -> list[str]:
110
- """简单的分词"""
165
+ """
166
+ Performs simple tokenization on the input text.
167
+
168
+ - **Parameters**:
169
+ - `text`: The input text to tokenize.
170
+
171
+ - **Returns**:
172
+ - A list of tokens extracted from the input text.
173
+ """
111
174
  # 这里使用简单的空格分词,实际应用中可以使用更复杂的分词方法
112
175
  return text.lower().split()
113
176
 
114
177
  def _update_vocab(self, tokens: list[str]):
115
- """更新词汇表"""
178
+ """
179
+ Updates the vocabulary with new tokens.
180
+
181
+ - **Description**:
182
+ - This method adds unique tokens from the input list to the vocabulary.
183
+ - It ensures that each token is only added once using a set for deduplication.
184
+
185
+ - **Parameters**:
186
+ - `tokens`: A list of strings representing the tokens to add to the vocabulary.
187
+ """
116
188
  for token in set(tokens): # 使用set去重
117
189
  if token not in self._vocab:
118
190
  self._vocab[token] = len(self._vocab)
119
191
 
120
192
  def _update_idf(self, tokens: list[str]):
121
- """更新IDF值"""
193
+ """
194
+ Updates the IDF values based on the tokens.
195
+
196
+ - **Description**:
197
+ - Increases the document count and updates the inverse document frequency (IDF) for each unique token.
198
+
199
+ - **Parameters**:
200
+ - `tokens`: A list of strings representing the tokens from a single document.
201
+ """
122
202
  self._doc_count += 1
123
203
  unique_tokens = set(tokens)
124
204
  for token in unique_tokens:
125
205
  self._idf[token] = self._idf.get(token, 0) + 1
126
206
 
127
207
  def _calculate_tf(self, tokens: list[str]) -> dict[str, float]:
128
- """计算词频(TF)"""
208
+ """
209
+ Calculates term frequency (TF) for the tokens.
210
+
211
+ - **Description**:
212
+ - Computes the frequency of each token within the provided list and normalizes it by the total number of tokens.
213
+
214
+ - **Parameters**:
215
+ - `tokens`: A list of strings representing the tokens to calculate TF for.
216
+
217
+ - **Returns**:
218
+ - A dictionary mapping each token to its normalized term frequency.
219
+ """
129
220
  tf = {}
130
221
  total_tokens = len(tokens)
131
222
  for token in tokens:
@@ -136,7 +227,18 @@ class SimpleEmbedding(Embeddings):
136
227
  return tf
137
228
 
138
229
  def _calculate_tfidf(self, tokens: list[str]) -> list[float]:
139
- """计算TF-IDF向量"""
230
+ """
231
+ Calculates the TF-IDF vector for the tokens.
232
+
233
+ - **Description**:
234
+ - Generates a vector representation of the input tokens using the Term Frequency-Inverse Document Frequency (TF-IDF) weighting scheme.
235
+
236
+ - **Parameters**:
237
+ - `tokens`: A list of strings representing the tokens to generate the TF-IDF vector for.
238
+
239
+ - **Returns**:
240
+ - A list of floating-point numbers representing the TF-IDF vector.
241
+ """
140
242
  vector = np.zeros(self.vector_dim)
141
243
  tf = self._calculate_tf(tokens)
142
244
 
@@ -154,13 +256,19 @@ class SimpleEmbedding(Embeddings):
154
256
  return list(vector)
155
257
 
156
258
  def _embed(self, text: str) -> list[float]:
157
- """生成文本的向量表示
259
+ """
260
+ Generates a vector representation for the input text.
158
261
 
159
- Args:
160
- text: 输入文本
262
+ - **Description**:
263
+ - Creates an embedding for the given text by first checking if it's already cached,
264
+ then tokenizing, updating the vocabulary and IDF, calculating the TF-IDF vector,
265
+ and finally caching the result.
161
266
 
162
- Returns:
163
- np.ndarray: 文本的向量表示
267
+ - **Parameters**:
268
+ - `text`: The input text to generate the vector for.
269
+
270
+ - **Returns**:
271
+ - A list of floating-point numbers representing the vector of the input text.
164
272
  """
165
273
  # 检查缓存
166
274
  text_hash = self._text_to_hash(text)
@@ -189,19 +297,30 @@ class SimpleEmbedding(Embeddings):
189
297
  return list(vector)
190
298
 
191
299
  def embed_documents(self, texts: list[str]) -> list[list[float]]:
192
- """Embed documents."""
300
+ """
301
+ Embeds a list of documents.
302
+
303
+ - **Parameters**:
304
+ - `texts`: A list of strings representing the documents to embed.
305
+
306
+ - **Returns**:
307
+ - A list of lists containing the embeddings of the documents.
308
+ """
193
309
  return [self._embed(text) for text in texts]
194
310
 
195
311
  def embed_query(self, text: str) -> list[float]:
196
- """Embed query text."""
312
+ """
313
+ Embeds a single query text.
314
+
315
+ - **Parameters**:
316
+ - `text`: The query text to embed.
317
+
318
+ - **Returns**:
319
+ - A list of floating-point numbers representing the embedding of the query text.
320
+ """
197
321
  return self._embed(text)
198
322
 
323
+
199
324
  if __name__ == "__main__":
200
- # se = SentenceEmbedding(
201
- # pretrained_model_name_or_path="ignore/BAAI--bge-m3", cache_dir="ignore"
202
- # )
203
325
  se = SimpleEmbedding()
204
326
  print(se.embed_query("hello world"))
205
- print(se.embed_query("hello world"))
206
- print(se.embed_query("hello world"))
207
- print(se.embed_query("hello world"))
pycityagent/llm/llm.py CHANGED
@@ -1,14 +1,9 @@
1
1
  """UrbanLLM: 智能能力类及其定义"""
2
2
 
3
+ import asyncio
3
4
  import json
4
5
  import logging
5
-
6
- from openai import APIConnectionError, AsyncOpenAI, OpenAI, OpenAIError
7
- from zhipuai import ZhipuAI
8
-
9
- logging.getLogger("zhipuai").setLevel(logging.WARNING)
10
-
11
- import asyncio
6
+ import time
12
7
  import os
13
8
  from http import HTTPStatus
14
9
  from io import BytesIO
@@ -17,21 +12,37 @@ from typing import Any, Optional, Union
17
12
  import dashscope
18
13
  import requests
19
14
  from dashscope import ImageSynthesis
15
+ from openai import APIConnectionError, AsyncOpenAI, OpenAI, OpenAIError
20
16
  from PIL import Image
17
+ from zhipuai import ZhipuAI
21
18
 
22
19
  from .llmconfig import *
23
20
  from .utils import *
24
21
 
22
+ logging.getLogger("zhipuai").setLevel(logging.WARNING)
25
23
  os.environ["GRPC_VERBOSITY"] = "ERROR"
26
24
 
25
+ __all__ = [
26
+ "LLM",
27
+ ]
28
+
27
29
 
28
30
  class LLM:
29
31
  """
30
- 大语言模型对象
31
- The LLM Object used by Agent(Soul)
32
+ Main class for the Large Language Model (LLM) object used by Agent(Soul).
33
+
34
+ - **Description**:
35
+ - This class manages configurations and interactions with different large language model APIs.
36
+ - It initializes clients based on the specified request type and handles token usage and consumption reporting.
32
37
  """
33
38
 
34
39
  def __init__(self, config: LLMConfig) -> None:
40
+ """
41
+ Initializes the LLM instance.
42
+
43
+ - **Parameters**:
44
+ - `config`: An instance of `LLMConfig` containing configuration settings for the LLM.
45
+ """
35
46
  self.config = config
36
47
  if config.text["request_type"] not in ["openai", "deepseek", "qwen", "zhipuai"]:
37
48
  raise ValueError("Invalid request type for text request")
@@ -40,12 +51,14 @@ class LLM:
40
51
  self.request_number = 0
41
52
  self.semaphore = None
42
53
  self._current_client_index = 0
54
+ self._log_list = []
43
55
 
44
56
  api_keys = self.config.text["api_key"]
45
57
  if not isinstance(api_keys, list):
46
58
  api_keys = [api_keys]
47
59
 
48
60
  self._aclients = []
61
+ self._client_usage = []
49
62
 
50
63
  for api_key in api_keys:
51
64
  if self.config.text["request_type"] == "openai":
@@ -69,11 +82,31 @@ class LLM:
69
82
  f"Unsupported `request_type` {self.config.text['request_type']}!"
70
83
  )
71
84
  self._aclients.append(client)
85
+ self._client_usage.append({
86
+ "prompt_tokens": 0,
87
+ "completion_tokens": 0,
88
+ "request_number": 0
89
+ })
90
+
91
+ def get_log_list(self):
92
+ return self._log_list
93
+
94
+ def clear_log_list(self):
95
+ self._log_list = []
72
96
 
73
97
  def set_semaphore(self, number_of_coroutine: int):
98
+ """
99
+ Sets the semaphore for controlling concurrent coroutines.
100
+
101
+ - **Parameters**:
102
+ - `number_of_coroutine`: The maximum number of concurrent coroutines allowed.
103
+ """
74
104
  self.semaphore = asyncio.Semaphore(number_of_coroutine)
75
105
 
76
106
  def clear_semaphore(self):
107
+ """
108
+ Clears the semaphore setting.
109
+ """
77
110
  self.semaphore = None
78
111
 
79
112
  def clear_used(self):
@@ -81,48 +114,80 @@ class LLM:
81
114
  clear the storage of used tokens to start a new log message
82
115
  Only support OpenAI category API right now, including OpenAI, Deepseek
83
116
  """
84
- self.prompt_tokens_used = 0
85
- self.completion_tokens_used = 0
86
- self.request_number = 0
117
+ for usage in self._client_usage:
118
+ usage["prompt_tokens"] = 0
119
+ usage["completion_tokens"] = 0
120
+ usage["request_number"] = 0
121
+
122
+ def get_consumption(self):
123
+ consumption = {}
124
+ for i, usage in enumerate(self._client_usage):
125
+ consumption[f"api-key-{i+1}"] = {
126
+ "total_tokens": usage["prompt_tokens"] + usage["completion_tokens"],
127
+ "request_number": usage["request_number"]
128
+ }
129
+ return consumption
87
130
 
88
131
  def show_consumption(
89
132
  self, input_price: Optional[float] = None, output_price: Optional[float] = None
90
133
  ):
91
134
  """
92
- if you give the input and output price of using model, this function will also calculate the consumption for you
135
+ Displays token usage and optionally calculates the estimated cost based on provided prices.
136
+
137
+ - **Parameters**:
138
+ - `input_price`: Price per million prompt tokens. Default is None.
139
+ - `output_price`: Price per million completion tokens. Default is None.
140
+
141
+ - **Returns**:
142
+ - A dictionary summarizing the token usage and, if applicable, the estimated cost.
93
143
  """
94
- total_token = self.prompt_tokens_used + self.completion_tokens_used
95
- if self.completion_tokens_used != 0:
96
- rate = self.prompt_tokens_used / self.completion_tokens_used
97
- else:
98
- rate = "nan"
99
- if self.request_number != 0:
100
- TcA = total_token / self.request_number
101
- else:
102
- TcA = "nan"
103
- out = f"""Request Number: {self.request_number}
104
- Token Usage:
105
- - Total tokens: {total_token}
106
- - Prompt tokens: {self.prompt_tokens_used}
107
- - Completion tokens: {self.completion_tokens_used}
108
- - Token per request: {TcA}
109
- - Prompt:Completion ratio: {rate}:1"""
110
- if input_price != None and output_price != None:
111
- consumption = (
112
- self.prompt_tokens_used / 1000000 * input_price
113
- + self.completion_tokens_used / 1000000 * output_price
114
- )
115
- out += f"\n - Cost Estimation: {consumption}"
116
- print(out)
117
- return {
118
- "total": total_token,
119
- "prompt": self.prompt_tokens_used,
120
- "completion": self.completion_tokens_used,
121
- "ratio": rate,
144
+ total_stats = {
145
+ "total": 0,
146
+ "prompt": 0,
147
+ "completion": 0,
148
+ "requests": 0
122
149
  }
150
+
151
+ for i, usage in enumerate(self._client_usage):
152
+ prompt_tokens = usage["prompt_tokens"]
153
+ completion_tokens = usage["completion_tokens"]
154
+ requests = usage["request_number"]
155
+ total_tokens = prompt_tokens + completion_tokens
156
+
157
+ total_stats["total"] += total_tokens
158
+ total_stats["prompt"] += prompt_tokens
159
+ total_stats["completion"] += completion_tokens
160
+ total_stats["requests"] += requests
161
+
162
+ rate = prompt_tokens / completion_tokens if completion_tokens != 0 else "nan"
163
+ tokens_per_request = total_tokens / requests if requests != 0 else "nan"
164
+
165
+ print(f"\nAPI Key #{i+1}:")
166
+ print(f"Request Number: {requests}")
167
+ print("Token Usage:")
168
+ print(f" - Total tokens: {total_tokens}")
169
+ print(f" - Prompt tokens: {prompt_tokens}")
170
+ print(f" - Completion tokens: {completion_tokens}")
171
+ print(f" - Token per request: {tokens_per_request}")
172
+ print(f" - Prompt:Completion ratio: {rate}:1")
173
+
174
+ if input_price is not None and output_price is not None:
175
+ consumption = (prompt_tokens / 1000000 * input_price +
176
+ completion_tokens / 1000000 * output_price)
177
+ print(f" - Cost Estimation: {consumption}")
178
+
179
+ return total_stats
123
180
 
124
181
  def _get_next_client(self):
125
- """获取下一个要使用的客户端"""
182
+ """
183
+ Retrieves the next client to be used for making requests.
184
+
185
+ - **Description**:
186
+ - This method cycles through the available clients in a round-robin fashion.
187
+
188
+ - **Returns**:
189
+ - The next client instance to be used for making requests.
190
+ """
126
191
  client = self._aclients[self._current_client_index]
127
192
  self._current_client_index = (self._current_client_index + 1) % len(
128
193
  self._aclients
@@ -143,8 +208,30 @@ Token Usage:
143
208
  tool_choice: Optional[dict[str, Any]] = None,
144
209
  ):
145
210
  """
146
- 异步版文本请求
211
+ Sends an asynchronous text request to the configured LLM API.
212
+
213
+ - **Description**:
214
+ - Attempts to send a text request up to `retries` times with exponential backoff on failure.
215
+ - Handles different request types and manages token usage statistics.
216
+
217
+ - **Parameters**:
218
+ - `dialog`: Messages to send as part of the chat completion request.
219
+ - `temperature`: Controls randomness in the model's output. Default is 1.
220
+ - `max_tokens`: Maximum number of tokens to generate in the response. Default is None.
221
+ - `top_p`: Limits the next token selection to a subset of tokens with a cumulative probability above this value. Default is None.
222
+ - `frequency_penalty`: Penalizes new tokens based on their existing frequency in the text so far. Default is None.
223
+ - `presence_penalty`: Penalizes new tokens based on whether they appear in the text so far. Default is None.
224
+ - `timeout`: Request timeout in seconds. Default is 300 seconds.
225
+ - `retries`: Number of retry attempts in case of failure. Default is 3.
226
+ - `tools`: List of dictionaries describing the tools that can be called by the model. Default is None.
227
+ - `tool_choice`: Dictionary specifying how the model should choose from the provided tools. Default is None.
228
+
229
+ - **Returns**:
230
+ - A string containing the message content or a dictionary with tool call arguments if tools are used.
231
+ - Raises exceptions if the request fails after all retry attempts.
147
232
  """
233
+ start_time = time.time()
234
+ log = {"request_time": start_time}
148
235
  if (
149
236
  self.config.text["request_type"] == "openai"
150
237
  or self.config.text["request_type"] == "deepseek"
@@ -153,57 +240,35 @@ Token Usage:
153
240
  for attempt in range(retries):
154
241
  try:
155
242
  client = self._get_next_client()
156
- if self.semaphore != None:
157
- async with self.semaphore:
158
- response = await client.chat.completions.create(
159
- model=self.config.text["model"],
160
- messages=dialog,
161
- temperature=temperature,
162
- max_tokens=max_tokens,
163
- top_p=top_p,
164
- frequency_penalty=frequency_penalty, # type: ignore
165
- presence_penalty=presence_penalty, # type: ignore
166
- stream=False,
167
- timeout=timeout,
168
- tools=tools,
169
- tool_choice=tool_choice,
170
- ) # type: ignore
171
- self.prompt_tokens_used += response.usage.prompt_tokens # type: ignore
172
- self.completion_tokens_used += response.usage.completion_tokens # type: ignore
173
- self.request_number += 1
174
- if tools and response.choices[0].message.tool_calls:
175
- return json.loads(
176
- response.choices[0]
177
- .message.tool_calls[0]
178
- .function.arguments
179
- )
180
- else:
181
- return response.choices[0].message.content
243
+ response = await client.chat.completions.create(
244
+ model=self.config.text["model"],
245
+ messages=dialog,
246
+ temperature=temperature,
247
+ max_tokens=max_tokens,
248
+ top_p=top_p,
249
+ frequency_penalty=frequency_penalty, # type: ignore
250
+ presence_penalty=presence_penalty, # type: ignore
251
+ stream=False,
252
+ timeout=timeout,
253
+ tools=tools,
254
+ tool_choice=tool_choice,
255
+ ) # type: ignore
256
+ self._client_usage[self._current_client_index]["prompt_tokens"] += response.usage.prompt_tokens # type: ignore
257
+ self._client_usage[self._current_client_index]["completion_tokens"] += response.usage.completion_tokens # type: ignore
258
+ self._client_usage[self._current_client_index]["request_number"] += 1
259
+ end_time = time.time()
260
+ log["consumption"] = end_time - start_time
261
+ log["input_tokens"] = response.usage.prompt_tokens
262
+ log["output_tokens"] = response.usage.completion_tokens
263
+ self._log_list.append(log)
264
+ if tools and response.choices[0].message.tool_calls:
265
+ return json.loads(
266
+ response.choices[0]
267
+ .message.tool_calls[0]
268
+ .function.arguments
269
+ )
182
270
  else:
183
- response = await client.chat.completions.create(
184
- model=self.config.text["model"],
185
- messages=dialog,
186
- temperature=temperature,
187
- max_tokens=max_tokens,
188
- top_p=top_p,
189
- frequency_penalty=frequency_penalty, # type: ignore
190
- presence_penalty=presence_penalty, # type: ignore
191
- stream=False,
192
- timeout=timeout,
193
- tools=tools,
194
- tool_choice=tool_choice,
195
- ) # type: ignore
196
- self.prompt_tokens_used += response.usage.prompt_tokens # type: ignore
197
- self.completion_tokens_used += response.usage.completion_tokens # type: ignore
198
- self.request_number += 1
199
- if tools and response.choices[0].message.tool_calls:
200
- return json.loads(
201
- response.choices[0]
202
- .message.tool_calls[0]
203
- .function.arguments
204
- )
205
- else:
206
- return response.choices[0].message.content
271
+ return response.choices[0].message.content
207
272
  except APIConnectionError as e:
208
273
  print("API connection error:", e)
209
274
  if attempt < retries - 1:
@@ -248,9 +313,13 @@ Token Usage:
248
313
  if task_status != "SUCCESS":
249
314
  raise Exception(f"Task failed with status: {task_status}")
250
315
 
251
- self.prompt_tokens_used += result_response.usage.prompt_tokens # type: ignore
252
- self.completion_tokens_used += result_response.usage.completion_tokens # type: ignore
253
- self.request_number += 1
316
+ self._client_usage[self._current_client_index]["prompt_tokens"] += result_response.usage.prompt_tokens # type: ignore
317
+ self._client_usage[self._current_client_index]["completion_tokens"] += result_response.usage.completion_tokens # type: ignore
318
+ self._client_usage[self._current_client_index]["request_number"] += 1
319
+ end_time = time.time()
320
+ log["used_time"] = end_time - start_time
321
+ log["token_consumption"] = result_response.usage.prompt_tokens + result_response.usage.completion_tokens
322
+ self._log_list.append(log)
254
323
  if tools and result_response.choices[0].message.tool_calls: # type: ignore
255
324
  return json.loads(
256
325
  result_response.choices[0] # type: ignore
@@ -273,15 +342,14 @@ Token Usage:
273
342
  self, img_path: Union[str, list[str]], prompt: Optional[str] = None
274
343
  ) -> str:
275
344
  """
276
- 图像理解
277
- Image understanding
345
+ Analyzes and understands images using external APIs.
278
346
 
279
- Args:
280
- - img_path (Union[str, list[str]]): 目标图像的路径, 既可以是一个路径也可以是包含多张图片路径的list. The path of selected Image
281
- - prompt (str): 理解提示词 - 例如理解方向. The understanding prompts
347
+ - **Args**:
348
+ img_path (Union[str, list[str]]): Path or list of paths to the images for analysis.
349
+ prompt (Optional[str]): Guidance text for understanding the images.
282
350
 
283
- Returns:
284
- - (str): the understanding content
351
+ - **Returns**:
352
+ str: The content derived from understanding the images.
285
353
  """
286
354
  ppt = "如何理解这幅图像?"
287
355
  if prompt != None:
@@ -354,16 +422,15 @@ Token Usage:
354
422
 
355
423
  async def img_generate(self, prompt: str, size: str = "512*512", quantity: int = 1):
356
424
  """
357
- 图像生成
358
- Image generation
425
+ Generates images based on a given prompt.
359
426
 
360
- Args:
361
- - prompt (str): 图像生成提示词. The image generation prompts
362
- - size (str): 生成图像尺寸, 默认为'512*512'. The image size, default: '512*512'
363
- - quantity (int): 生成图像数量, 默认为1. The quantity of generated images, default: 1
427
+ - **Args**:
428
+ prompt (str): Prompt for generating images.
429
+ size (str): Size of the generated images, default is '512*512'.
430
+ quantity (int): Number of images to generate, default is 1.
364
431
 
365
- Returns:
366
- - (list[PIL.Image.Image]): 生成的图像列表. The list of generated Images.
432
+ - **Returns**:
433
+ list[PIL.Image.Image]: List of generated PIL Image objects.
367
434
  """
368
435
  rsp = ImageSynthesis.call(
369
436
  model=self.config.image_g["model"],
@@ -1,3 +1,8 @@
1
+ __all__ = [
2
+ "LLMConfig",
3
+ ]
4
+
5
+
1
6
  class LLMConfig:
2
7
  """
3
8
  大语言模型相关配置