@@ -37,6 +37,9 @@ def __init__(
37
37
self .celestial_objects = []
38
38
self .celestial_object_names = []
39
39
self .quantum_drives = []
40
+ self .shops = []
41
+ self .shop_names = []
42
+ self .shop_parent_names = []
40
43
41
44
async def validate (self ) -> list [WingmanInitializationError ]:
42
45
errors = await super ().validate ()
@@ -78,6 +81,19 @@ async def _prepare_data(self):
78
81
"vehiclecomponent" , {"typeFilter" : 8 }
79
82
)
80
83
84
+ self .shops = await self ._fetch_data ("shop" )
85
+ self .shop_names = [shop ["name" ] for shop in self .shops ]
86
+
87
+ # Remove duplicate shop names
88
+ self .shop_names = list (dict .fromkeys (self .shop_names ))
89
+
90
+ self .shop_parent_names = [
91
+ shop ["parent" ]["name" ] for shop in self .shops if shop ["parent" ]
92
+ ]
93
+
94
+ # Remove duplicate parent names
95
+ self .shop_parent_names = list (dict .fromkeys (self .shop_parent_names ))
96
+
81
97
async def _fetch_data (
82
98
self , endpoint : str , params : Optional [dict [str , any ]] = None
83
99
) -> list [dict [str , any ]]:
@@ -113,6 +129,17 @@ async def execute_tool(
113
129
function_response = await self ._get_best_trading_route (** parameters )
114
130
if tool_name == "get_ship_information" :
115
131
function_response = await self ._get_ship_information (** parameters )
132
+ if tool_name == "get_trading_information_of_specific_shop" :
133
+ function_response = await self ._get_trading_information_of_specific_shop (
134
+ ** parameters
135
+ )
136
+ if tool_name == "get_trading_shop_information_for_celestial_objects" :
137
+ function_response = (
138
+ await self ._get_trading_shop_information_for_celestial_objects (
139
+ ** parameters
140
+ )
141
+ )
142
+
116
143
return function_response , instant_response
117
144
118
145
async def is_waiting_response_needed (self , tool_name : str ) -> bool :
@@ -135,9 +162,9 @@ def get_tools(self) -> list[tuple[str, dict]]:
135
162
"type" : "string" ,
136
163
"enum" : self .celestial_object_names ,
137
164
},
138
- "moneyToSpend " : {"type" : "number" },
165
+ "money_to_spend " : {"type" : "number" },
139
166
},
140
- "required" : ["ship" , "position" , "moneyToSpend " ],
167
+ "required" : ["ship" , "position" , "money_to_spend " ],
141
168
},
142
169
},
143
170
},
@@ -159,10 +186,94 @@ def get_tools(self) -> list[tuple[str, dict]]:
159
186
},
160
187
},
161
188
),
189
+ (
190
+ "get_trading_information_of_specific_shop" ,
191
+ {
192
+ "type" : "function" ,
193
+ "function" : {
194
+ "name" : "get_trading_information_of_specific_shop" ,
195
+ "description" : "Gives trading information about the given shop, like which commodities you can sell or buy and for which price. If the name of the shop is not unique, you have to specify the celestial object." ,
196
+ "parameters" : {
197
+ "type" : "object" ,
198
+ "properties" : {
199
+ "shop" : {"type" : "string" , "enum" : self .shop_names },
200
+ },
201
+ "required" : ["shop" ],
202
+ },
203
+ },
204
+ },
205
+ ),
206
+ (
207
+ "get_trading_shop_information_for_celestial_objects" ,
208
+ {
209
+ "type" : "function" ,
210
+ "function" : {
211
+ "name" : "get_trading_shop_information_for_celestial_objects" ,
212
+ "description" : "Gives trading information about the given celestial object, like which commodities you can sell or buy at which shop and for which price. All shops with shop items on that celestial object will be returned." ,
213
+ "parameters" : {
214
+ "type" : "object" ,
215
+ "properties" : {
216
+ "celestial_object" : {
217
+ "type" : "string" ,
218
+ "enum" : self .celestial_object_names ,
219
+ },
220
+ },
221
+ "required" : ["celestial_object" ],
222
+ },
223
+ },
224
+ },
225
+ ),
162
226
]
163
227
164
228
return tools
165
229
230
+ async def _get_trading_shop_information_for_celestial_objects (
231
+ self , celestial_object : str
232
+ ) -> str :
233
+ object_id = self ._get_celestial_object_id (celestial_object )
234
+
235
+ if not object_id :
236
+ return f"Could not find celestial object '{ celestial_object } ' in the StarHead database."
237
+
238
+ shops = await self ._fetch_data (f"shop?celestialObjectFilter={ object_id } " )
239
+
240
+ shop_items = {}
241
+ for shop in shops :
242
+ items = await self ._fetch_data (f"shop/{ shop ['id' ]} /items" )
243
+ for item in items :
244
+ item ["pricePerItem" ] = item ["pricePerItem" ] * 100
245
+ item ["tradeType" ] = (
246
+ "Sold by store" if item ["tradeType" ] == "buy" else "The shop buys"
247
+ )
248
+ shop_items [f"{ shop ['parent' ]['name' ]} - { shop ['name' ]} " ] = items
249
+
250
+ shop_details = json .dumps (shop_items )
251
+ return shop_details
252
+
253
+ async def _get_trading_information_of_specific_shop (self , shop : str = None ) -> str :
254
+ # Get all shops with the given name
255
+ shops = [
256
+ shop for shop in self .shops if shop ["name" ].lower () == shop ["name" ].lower ()
257
+ ]
258
+
259
+ # Check if there are multiple shops with the same name
260
+ if len (shops ) > 1 :
261
+ return f"Multiple shops with the name '{ shop } ' found. Please specify the celestial object."
262
+
263
+ if not shops :
264
+ return f"Could not find shop '{ shop } ' in the StarHead database."
265
+
266
+ shop_items = {}
267
+
268
+ for shop in shops :
269
+ items = await self ._fetch_data (f"shop/{ shop ['id' ]} /items" )
270
+ for item in items :
271
+ item ["pricePerItem" ] = item ["pricePerItem" ] * 100
272
+ shop_items [shop ["name" ]] = items
273
+
274
+ shop_details = json .dumps (items )
275
+ return shop_details
276
+
166
277
async def _get_ship_information (self , ship : str ) -> str :
167
278
try :
168
279
response = requests .get (
@@ -177,7 +288,7 @@ async def _get_ship_information(self, ship: str) -> str:
177
288
return ship_details
178
289
179
290
async def _get_best_trading_route (
180
- self , ship : str , position : str , moneyToSpend : float
291
+ self , ship : str , position : str , money_to_spend : float
181
292
) -> str :
182
293
"""Calculates the best trading route for the specified ship and position.
183
294
Note that the function arguments have to match the funtion_args from OpenAI, hence the camelCase!
@@ -195,7 +306,7 @@ async def _get_best_trading_route(
195
306
"startCelestialObjectId" : celestial_object_id ,
196
307
"quantumDriveId" : qd ["id" ] if qd else None ,
197
308
"maxAvailablScu" : cargo ,
198
- "maxAvailableMoney" : moneyToSpend ,
309
+ "maxAvailableMoney" : money_to_spend ,
199
310
"useOnlyWeaponFreeZones" : False ,
200
311
"onlySingleSections" : True ,
201
312
}
@@ -215,7 +326,7 @@ async def _get_best_trading_route(
215
326
if parsed_response :
216
327
section = parsed_response [0 ]
217
328
return json .dumps (section )
218
- return f"No route found for ship '{ ship } ' at '{ position } ' with '{ moneyToSpend } ' aUEC."
329
+ return f"No route found for ship '{ ship } ' at '{ position } ' with '{ money_to_spend } ' aUEC."
219
330
220
331
def _get_celestial_object_id (self , name : str ) -> Optional [int ]:
221
332
"""Finds the ID of the celestial object with the specified name."""
0 commit comments