22
33import json
44import logging
5+ from ast import literal_eval
56from pathlib import Path
67from typing import Dict , List , Optional , Union
78
89from kili .services .asset_import .helpers import SEPARATOR
910from kili .services .export .exceptions import NotCompatibleInputType
1011from kili .services .export .format .base import AbstractExporter
12+ from kili .services .export .format .llm .types import ExportLLMItem , RankingValue
1113from kili .services .types import Job
1214
1315
@@ -44,14 +46,48 @@ def process_and_save(
4446 self , assets : List [Dict ], output_filename : Path
4547 ) -> Optional [List [Dict [str , Union [List [str ], str ]]]]:
4648 """LLM specific process and save."""
47- result = self ._process (assets )
49+ result = self .process (assets )
4850 self ._save_assets_export (result , output_filename )
4951
5052 def process (self , assets : List [Dict ]) -> List [Dict [str , Union [List [str ], str ]]]:
5153 """LLM specific process."""
52- return self ._process (assets )
54+ if self .label_format == "llm_v1" :
55+ return self ._process_llm_v1 (assets )
56+ return self ._process_llm_dynamic_v1 (assets )
5357
54- def _process (self , assets : List [Dict ]) -> List [Dict [str , Union [List [str ], str ]]]:
58+ def _process_llm_dynamic_v1 (self , assets : List [Dict ]) -> List [Dict [str , Union [List [str ], str ]]]:
59+ result = []
60+ for asset in assets :
61+ step_number = _count_step (asset )
62+ label = asset ["latestLabel" ]
63+ steps = {}
64+ context = []
65+ formatted_asset = _format_raw_data (asset , all_model_keys = True )
66+ for i in range (step_number ):
67+ steps [f"{ i } " ] = {
68+ "raw_data" : context + _format_raw_data (asset , i ),
69+ "status" : asset ["status" ],
70+ "external_id" : asset ["externalId" ],
71+ "metadata" : asset ["jsonMetadata" ],
72+ "labels" : [
73+ {
74+ "author" : label ["author" ]["email" ],
75+ "created_at" : label ["createdAt" ],
76+ "label_type" : label ["labelType" ],
77+ "label" : _format_json_response_dynamic (
78+ self .project ["jsonInterface" ]["jobs" ], label ["jsonResponse" ], i
79+ ),
80+ }
81+ ],
82+ }
83+ next_context = _get_next_step_context (formatted_asset , label ["jsonResponse" ], i )
84+ context = context + next_context
85+
86+ if step_number > 0 :
87+ result .append (steps )
88+ return result
89+
90+ def _process_llm_v1 (self , assets : List [Dict ]) -> List [Dict [str , Union [List [str ], str ]]]:
5591 result = []
5692 for asset in assets :
5793 result .append (
@@ -60,26 +96,87 @@ def _process(self, assets: List[Dict]) -> List[Dict[str, Union[List[str], str]]]
6096 "status" : asset ["status" ],
6197 "external_id" : asset ["externalId" ],
6298 "metadata" : asset ["jsonMetadata" ],
63- "labels" : [
64- list (
65- map (
66- lambda label : {
67- "author" : label ["author" ]["email" ],
68- "created_at" : label ["createdAt" ],
69- "label_type" : label ["labelType" ],
70- "label" : _format_json_response (
71- self .project ["jsonInterface" ]["jobs" ], label ["jsonResponse" ]
72- ),
73- },
74- asset ["labels" ],
75- )
99+ "labels" : list (
100+ map (
101+ lambda label : {
102+ "author" : label ["author" ]["email" ],
103+ "created_at" : label ["createdAt" ],
104+ "label_type" : label ["labelType" ],
105+ "label" : _format_json_response (
106+ self .project ["jsonInterface" ]["jobs" ], label ["jsonResponse" ]
107+ ),
108+ },
109+ asset ["labels" ],
76110 )
77- ] ,
111+ ) ,
78112 }
79113 )
80114 return result
81115
82116
117+ def _get_step_ranking_value (json_response : Dict , step_number : int ) -> RankingValue :
118+ prefix = f"STEP_{ step_number + 1 } _"
119+ for category in json_response ["CLASSIFICATION_JOB" ]["categories" ]:
120+ if category ["name" ] != f"STEP_{ step_number + 1 } " :
121+ continue
122+
123+ for children_name , children_value in category ["children" ].items ():
124+ if children_name == f"STEP_{ step_number + 1 } _RANKING" :
125+ raw_value = children_value ["categories" ][0 ]["name" ]
126+ return raw_value [len (prefix ) :]
127+ return RankingValue .TIE
128+
129+
130+ def _get_next_step_context (
131+ formatted_asset : List [ExportLLMItem ], json_response : Dict , step_number : int
132+ ) -> List [ExportLLMItem ]:
133+ context = []
134+ skipped_context = 0
135+ completion_index = 0
136+ ranking = _get_step_ranking_value (json_response , step_number )
137+ for item in formatted_asset :
138+ if skipped_context > step_number :
139+ break
140+
141+ if skipped_context == step_number :
142+ if item ["role" ] == "user" :
143+ context .append (item )
144+ else :
145+ if completion_index == 0 and ranking in ["A_1" , "A_2" , "A_3" , "TIE" ]:
146+ context .append (item )
147+ break
148+ if completion_index == 1 and ranking in ["B_1" , "B_2" , "B_3" ]:
149+ context .append (item )
150+ break
151+ completion_index += 1
152+
153+ if item ["role" ] == "assistant" :
154+ skipped_context += 1
155+
156+ return context
157+
158+
159+ def _count_step (asset : Dict ) -> int :
160+ label = asset ["latestLabel" ]
161+ if "jsonResponse" not in label and "CLASSIFICATION_JOB" not in label ["jsonResponse" ]:
162+ return 0
163+ return len (label ["jsonResponse" ]["CLASSIFICATION_JOB" ]["categories" ])
164+
165+
166+ def _format_json_response_dynamic (
167+ jobs_config : Dict , json_response : Dict , step_number : int
168+ ) -> Dict [str , Union [str , List [str ]]]:
169+ # check subjobs of the step
170+ job_step = f"STEP_{ step_number + 1 } "
171+ for item in json_response ["CLASSIFICATION_JOB" ]["categories" ]:
172+ if item ["name" ] != job_step :
173+ continue
174+ response_step = _format_json_response (jobs_config , item ["children" ])
175+ formatted_response = literal_eval (str (response_step ).replace (job_step + "_" , "" ))
176+ return formatted_response
177+ return {}
178+
179+
83180def _format_json_response (
84181 jobs_config : Dict , json_response : Dict
85182) -> Dict [str , Union [str , List [str ]]]:
@@ -104,7 +201,9 @@ def _format_json_response(
104201 return result
105202
106203
107- def _format_raw_data (asset ) -> List [Dict ]:
204+ def _format_raw_data (
205+ asset , step_number : Optional [int ] = None , all_model_keys : Optional [bool ] = False
206+ ) -> List [ExportLLMItem ]:
108207 raw_data = []
109208
110209 chat_id = asset ["jsonMetadata" ].get ("chat_id" , None )
@@ -115,6 +214,8 @@ def _format_raw_data(asset) -> List[Dict]:
115214 and len (asset ["jsonMetadata" ]["chat_item_ids" ]) > 0
116215 ):
117216 chat_items_ids = str .split (asset ["jsonMetadata" ]["chat_item_ids" ], SEPARATOR )
217+ if step_number is not None :
218+ chat_items_ids = chat_items_ids [step_number * 3 :]
118219 else :
119220 chat_items_ids = []
120221
@@ -131,25 +232,34 @@ def _format_raw_data(asset) -> List[Dict]:
131232 data = json .load (file )
132233 version = data .get ("version" , None )
133234 if version == "0.1" :
134- for index , prompt in enumerate (data ["prompts" ]):
235+ prompts = data ["prompts" ]
236+ if step_number is not None :
237+ prompts = [prompts [step_number ]]
238+ for index , prompt in enumerate (prompts ):
135239 raw_data .append (
136- {
137- "role" : prompt .get ("title" , "user" ),
138- "content" : prompt ["prompt" ],
139- "id" : _safe_pop (chat_items_ids ),
140- "chat_id" : chat_id ,
141- "model" : None ,
142- }
240+ ExportLLMItem (
241+ {
242+ "role" : prompt .get ("title" , "user" ),
243+ "content" : prompt ["prompt" ],
244+ "id" : _safe_pop (chat_items_ids ),
245+ "chat_id" : chat_id ,
246+ "model" : None ,
247+ }
248+ )
143249 )
144250 raw_data .extend (
145- {
146- "role" : completion .get ("title" , "assistant" ),
147- "content" : completion ["content" ],
148- "id" : _safe_pop (chat_items_ids ),
149- "chat_id" : chat_id ,
150- "model" : _safe_pop (models ) if index == len (data ["prompts" ]) - 1 else None ,
151- }
152- for completion in prompt ["completions" ]
251+ ExportLLMItem (
252+ {
253+ "role" : completion .get ("title" , "assistant" ),
254+ "content" : completion ["content" ],
255+ "id" : _safe_pop (chat_items_ids ),
256+ "chat_id" : chat_id ,
257+ "model" : models [index_completion ]
258+ if (index == len (prompts ) - 1 or all_model_keys )
259+ else None ,
260+ }
261+ )
262+ for index_completion , completion in enumerate (prompt ["completions" ])
153263 )
154264 else :
155265 raise ValueError (f"Version { version } not supported" )
0 commit comments