@@ -86,6 +86,7 @@ def __init__(
86
86
final_summary_prompt : str = FINAL_SUMMARY_PROMPT , # 最終要約用プロンプト
87
87
repository_description_prompt : str = None , # リポジトリ説明プロンプト
88
88
request_timeout : int = 180 , # 1回のリクエストに対するタイムアウト秒数(CLIから調整可能、デフォルト180)
89
+ max_input_tokens : int = None , # LLMの最大入力トークン数
89
90
):
90
91
self .model = model
91
92
self .temperature = temperature
@@ -94,6 +95,7 @@ def __init__(
94
95
self .final_summary_prompt = final_summary_prompt
95
96
# repository_description_prompt が None または空文字列の場合はそのまま None または空文字列を保持
96
97
self .repository_description_prompt = repository_description_prompt
98
+ self .max_input_tokens = max_input_tokens # 最大生成トークン数を設定
97
99
98
100
# 利用可能なツールを設定
99
101
self .available_tools = available_tools or []
@@ -121,6 +123,86 @@ def __init__(
121
123
else 0 ,
122
124
)
123
125
126
+ async def _get_messages_for_llm (self ) -> List [Dict [str , Any ]]:
127
+ """
128
+ LLMに渡すメッセージリストを作成する。トークン数制限を考慮する。
129
+
130
+ Returns:
131
+ LLMに渡すメッセージの辞書リスト。
132
+ """
133
+ if not self .conversation_history :
134
+ return []
135
+
136
+ messages_to_send = []
137
+ current_tokens = 0
138
+
139
+ # 1. 最初のシステムメッセージと最初のユーザープロンプトは必須
140
+ # 最初のシステムメッセージ
141
+ if self .conversation_history [0 ].role == "system" :
142
+ system_message = self .conversation_history [0 ].to_dict ()
143
+ messages_to_send .append (system_message )
144
+ if self .max_input_tokens is not None :
145
+ current_tokens += litellm .token_counter (
146
+ model = self .model , messages = [system_message ]
147
+ )
148
+
149
+ # 最初のユーザーメッセージ (システムメッセージの次にあると仮定)
150
+ if (
151
+ len (self .conversation_history ) > 1
152
+ and self .conversation_history [1 ].role == "user"
153
+ ):
154
+ user_message = self .conversation_history [1 ].to_dict ()
155
+ # 既にシステムメッセージが追加されているか確認
156
+ if not messages_to_send or messages_to_send [- 1 ] != user_message :
157
+ # トークンチェック
158
+ if self .max_input_tokens is not None :
159
+ user_message_tokens = litellm .token_counter (
160
+ model = self .model , messages = [user_message ]
161
+ )
162
+ if current_tokens + user_message_tokens <= self .max_input_tokens :
163
+ messages_to_send .append (user_message )
164
+ current_tokens += user_message_tokens
165
+ else :
166
+ raise ValueError (
167
+ f"最初のユーザーメッセージがトークン制限を超えています。必要なトークン数: { user_message_tokens } , 現在のトークン数: { current_tokens } , 最大トークン数: { self .max_input_tokens } "
168
+ )
169
+ else :
170
+ messages_to_send .append (user_message )
171
+
172
+ # 2. 最新の会話履歴からトークン制限を超えない範囲で追加
173
+ # 必須メッセージ以降の履歴を取得 (必須メッセージが2つと仮定)
174
+ remaining_history = self .conversation_history [2 :]
175
+
176
+ temp_recent_messages : list [Dict [str , Any ]] = []
177
+ for msg in reversed (remaining_history ):
178
+ msg_dict = msg .to_dict ()
179
+ if self .max_input_tokens is not None :
180
+ msg_tokens = litellm .token_counter (
181
+ model = self .model , messages = [msg_dict ]
182
+ )
183
+ if current_tokens + msg_tokens <= self .max_input_tokens :
184
+ temp_recent_messages .insert (0 , msg_dict ) # 逆順なので先頭に追加
185
+ current_tokens += msg_tokens
186
+ else :
187
+ # トークン制限に達したらループを抜ける
188
+ logger .debug (
189
+ "トークン制限に達したため、これ以上過去のメッセージは含めません。" ,
190
+ message_content = msg_dict .get ("content" , "" )[:50 ],
191
+ required_tokens = msg_tokens ,
192
+ current_tokens = current_tokens ,
193
+ max_tokens = self .max_input_tokens ,
194
+ )
195
+ break
196
+ else :
197
+ temp_recent_messages .insert (0 , msg_dict )
198
+
199
+ messages_to_send .extend (temp_recent_messages )
200
+
201
+ logger .debug (
202
+ f"LLMに渡すメッセージ数: { len (messages_to_send )} , トークン数: { current_tokens if self .max_input_tokens is not None else 'N/A' } "
203
+ )
204
+ return messages_to_send
205
+
124
206
async def _execute_tool (self , tool_name : str , arguments : Dict [str , Any ]) -> str :
125
207
"""指定されたツールを実行してその結果を返す"""
126
208
logger .debug ("Executing tool" , tool_name = tool_name , arguments = arguments )
@@ -209,7 +291,9 @@ async def _planning_phase(self, prompt: str) -> None:
209
291
try :
210
292
response = await litellm .acompletion (
211
293
model = self .model ,
212
- messages = [msg .to_dict () for msg in self .conversation_history ],
294
+ messages = [
295
+ msg .to_dict () for msg in self .conversation_history
296
+ ], # プランニングフェーズでは全履歴を使用することが多い
213
297
temperature = self .temperature ,
214
298
tools = self .tools , # 更新されたツールリストを使用
215
299
timeout = self .request_timeout , # 1回のリクエスト用タイムアウト
@@ -287,7 +371,7 @@ async def _execution_phase(self) -> bool:
287
371
288
372
response = await litellm .acompletion (
289
373
model = self .model ,
290
- messages = [ msg . to_dict () for msg in self . conversation_history ],
374
+ messages = await self . _get_messages_for_llm (), # 引数を削除
291
375
temperature = self .temperature ,
292
376
tools = self .tools , # 更新されたツールリストを使用
293
377
timeout = self .request_timeout , # 1回のリクエスト用タイムアウト
@@ -390,7 +474,7 @@ async def _execution_phase(self) -> bool:
390
474
logger .debug ("Getting next actions from LLM after tool executions" )
391
475
response = await litellm .acompletion (
392
476
model = self .model ,
393
- messages = [ msg . to_dict () for msg in self . conversation_history ],
477
+ messages = await self . _get_messages_for_llm (), # 引数を削除
394
478
temperature = self .temperature ,
395
479
tools = self .tools , # 更新されたツールリストを使用
396
480
timeout = self .request_timeout , # 1回のリクエスト用タイムアウト
@@ -474,9 +558,9 @@ async def run(self, prompt: str) -> str:
474
558
475
559
final_response = await litellm .acompletion (
476
560
model = self .model ,
477
- messages = [ msg . to_dict () for msg in self .conversation_history ] ,
561
+ messages = await self ._get_messages_for_llm () ,
478
562
temperature = self .temperature ,
479
- tools = self .tools , # ツールパラメータを追加
563
+ tools = self .tools , # 使わないけど、ツールリストを提供して、Anthropicの要件を満たす
480
564
timeout = self .request_timeout , # 1回のリクエスト用タイムアウト
481
565
)
482
566
logger .debug (
0 commit comments