File size: 21,346 Bytes
7744ffd e0774f3 7744ffd 360052a 89523ef 6dbb429 de6114e e0774f3 a525d9f 7744ffd e0774f3 4fe308d 0c7d480 5a083d5 feb4046 aaeb0d7 263ab40 5a083d5 96d3248 feb4046 5a083d5 96d3248 5a083d5 360052a f7278cf 360052a f7278cf 360052a f7278cf 360052a 5a083d5 360052a 6f97f98 360052a 6f97f98 a91b089 4be9a2e 360052a ce95d69 4e09ab2 a91b089 bc25bd7 360052a ce95d69 360052a bc25bd7 e0774f3 85af614 360052a ccf0c65 360052a d278c68 7744ffd e0774f3 7744ffd 460cfd4 06dde0f 460cfd4 06dde0f 460cfd4 ec69e62 460cfd4 5a083d5 460cfd4 5a083d5 06dde0f 5a083d5 ec69e62 f7278cf 5a083d5 f7278cf 7e06873 e0774f3 7744ffd e0774f3 ccf0c65 a4907a2 ccf0c65 a4907a2 ccf0c65 a4907a2 ccf0c65 a4907a2 ccf0c65 a4907a2 ccf0c65 a4907a2 ccf0c65 a4907a2 7744ffd ccf0c65 7744ffd 9c22eb2 f1c6894 4fe308d b37c269 0c7d480 4fe308d 0c7d480 b37c269 e0774f3 5f0757f d35e6e6 5f0757f ccf0c65 ae688e0 3189a9e 0a44ca5 39a4aa4 5f0757f 3ef13e8 e9d7866 67ce899 9bfa686 3ce9eeb 5f0757f 67ce899 c425f16 ccf0c65 a4907a2 ccf0c65 a4907a2 0f80413 6dbb429 4fe308d 0c7d480 6dbb429 0c7d480 ae688e0 0c7d480 6dbb429 0c7d480 89523ef 3ef13e8 a4907a2 89523ef 6dbb429 9c22eb2 5f0757f ccf0c65 89523ef 5f0757f e0774f3 0f80413 6dbb429 127a310 e0774f3 7744ffd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | import os
from pydantic import BaseModel, Field, validator, ValidationError
import gradio as gr
from openai import OpenAI
from typing import List, Dict, Any, Optional, Literal, Union
from enum import Enum
from gradio_toggle import Toggle
from dicttoxml import dicttoxml
import json
# adding comment
# Chatbot model
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
client = OpenAI()
original_outputs = []
xml_outputs = []
# These are the necessary components that make up the Trials
#class Variables(BaseModel):
# controlled: List[str] = Field(..., title="Controlled Variables", description="A list of controlled variables, which will be constant (controlled) across all trials")
# independent: List[str] = Field(..., title="Independent Variables", description="A list of independent variables (ie treatments), which will be intentionally varied across one or more trials")
# outcome: List[str] = Field(..., title="Outcome Variables", description="A list of outcome variables (ie dependent or response variables)")
class Treatment(BaseModel):
name: str = Field(..., title="Name", description="The treatment name")
description: str = Field(..., title="Description", description="The treatment description, including the conditions within this treatment")
crops: List[str] = Field(..., title="Crops", description="A list of crops being tested in this treatment")
fields: List[str] = Field(..., title="Fields", description="A list of fields in which this treatment has occured or will occur")
#learnings: List[str] = Field(..., title="Learnings", description="A list of lessons learned from this experiment")
#variables: Variables = Field(..., title="Variables", description="Variables (ie factors) in this experiment. Some variables are constant (controlled) and some will vary in order to learn something (independent)")
#confoundingFactors: List[str] = Field(..., title="Confounding Factors", description="A list of factors which may impact the outcomes of the experiment that were not planned for")
class Trial(BaseModel):
name: str = Field(..., title="Name", description="The name of this trial")
description: str = Field(..., title="Description", description="A description of this trial")
treatments: List[Treatment] = Field(..., title="Treatments", description="A list of different treatments (strips or blocks with the same conditions applied) performed by the partner")
#################################################################################
# These are the necessary components that make up the Interactions
class Role(str, Enum):
PARTNER = 'partner'
STAFF = 'staff'
AGRONOMIST = 'agronomist'
OTHER = 'other'
class Person(BaseModel):
name: str = Field(..., title="Name", description="Name of this person")
role: Role = Field(..., title="Role", description="Role of this person")
class Interactions(BaseModel):
people: List[Person] = Field(..., title="People", description="People involved or mentioned during interaction")
date: str = Field(..., title="Date of current interaction", description="Date of the interaction")
nextMeeting: str = Field(..., title="Date of next meeting", description="Proposed date of the next future interaction")
nextSteps: List[str] = Field(..., title="Next Steps", description="List of individual next steps derived from the interaction")
summary: str = Field(..., title="Summary", description="Summary of the interaction")
#################################################################################
# These are the components for Farm Activities, Fields, and Plantings
class Status(str, Enum):
ACTIVE = 'active'
ARCHIVED = 'archived'
# Depending on how well this works, come back and hard-code this based on some parameter(s)
class Convention(str, Enum):
ACTIVITY = 'log--activity'
OBSERVATION = 'log--observation'
FLAMING = 'log--activity--flaming'
GRAZING = 'log--activity--grazing'
MOWING = 'log--activity--mowing'
SOLARIZATION = 'log--activity--solarization'
TERMINATION = 'log--activity--termination'
TILLAGE = 'log--activity--tillage'
HARVEST = 'log--activity--harvest'
HERBICIDE = 'log--input--herbicide_or_pesticide'
IRRIGATION = 'log--input--irrigation'
LIME = 'log--input--lime'
ORGANIC = 'log--input--organic_matter'
SEEDTREAT = 'log--input--seed_treatment'
SEEDLINGTREAT = 'log--input--seedling_treatment'
MODUS = 'log--lab_test--modus_lab_test'
SEEDING = 'log--seeding--seeding'
TRANSPLANT = 'log--transplanting--transplant'
class Structure(str, Enum):
CLAY = 'clay'
SANDYCLAY = 'sandy clay'
SILTYCLAY = 'silty clay'
SANDYCLAYLOAM = 'sandy clay loam'
SILYCLAMLOAM = 'silty clay loam'
CLAYLOAM = 'clay loam'
SANDYLOAM = 'sandy loam'
SILTLOAM = 'silt loam'
LOAM = 'loam'
LOAMYSAND = 'loamy sand'
SAND = 'sand'
SILT = 'silt'
class Log(BaseModel):
convention: Convention = Field(..., title="Logs", description="This log's convention (i.e. this log's category or type)")
date: str = Field(..., title="Date", description="Date the log (i.e. action of the activity or input) was performed")
description: str = Field(..., title="Description", description="A description of the details of the log (i.e. details about farm activity performed")
class Soil(BaseModel):
description: str = Field(..., title="Description", description="A general description of the soil")
structure: List[Structure] = Field(..., title="Structure", description="The structure of the soil using options from the major soil texture classes (sand, clay, silt)")
biology: str = Field(..., title="Biology", description="Biological activity levels of the soil, including fluffiness, worms and bugs, and other evidence of soil biological activity")
class Yield(BaseModel):
quantity: str = Field(..., title="Quantity", description="A description of the total yield (harvested amount) from this planting, including units when available")
quality: str = Field(..., title="Quality", description="The product quality of the harvest. For example, small or large fruits, sweet or tart flavor, easily molding or containing mold, high number of product seconds, etc.")
# It breaks if soil and yield aren't lists for some reason
class Planting(BaseModel):
name: str = Field(..., title="Name", description="The name of the planting")
status: Status = Field(..., title="Status", description="The status of the planting. \"active\" is a planting which is currently still in the field. \"archived\" is a planting which is no longer in the field (has been terminated or harvested)")
crop: List[str] = Field(..., title="Crop", description="A list of the crops in this planting")
variety: List[str] = Field(..., title="Variety", description="A list of the crop varieties in this planting")
logs: List[Log] = Field(..., title="Logs", description="A list of all the logs that are associated with the farm activities")
soil: List[Soil] = Field(..., title="Soil", description="A single soil profile for this planting, containing only one soil description")
yield_: List[Yield] = Field(..., title="Yield", description="One set of quantitative and qualitative yield observations for this planting")
class FarmActivities(BaseModel):
name: str = Field(..., title="Name", description="The name of the agricultural field.")
description: str = Field(..., title="Description", description="The description of the agricultural field.")
plantings: List[Planting] = Field(..., title="Plantings", description="All of the plantings which have occurred on this field.")
# These are extra for the modular approach
class FarmActivitiesLite(BaseModel):
name: str = Field(..., title="Name", description="The name of the agricultural field.")
description: str = Field(..., title="Description", description="The description of the agricultural field.")
class PlantingLite(BaseModel):
name: str = Field(..., title="Name", description="The name of the planting")
status: Status = Field(..., title="Status", description="The status of the planting. \"active\" is a planting which is currently still in the field. \"archived\" is a planting which is no longer in the field (has been terminated or harvested)")
crop: List[str] = Field(..., title="Crop", description="A list of the crops in this planting")
variety: List[str] = Field(..., title="Variety", description="A list of the crop varieties in this planting")
# This is to make stuff happen
def generate_json(specification, model_version):
"""
Function to prompt OpenAI API to generate structured JSON output.
"""
try:
#Call OpenAI API to generate structured output based on prompt
farm_info_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the farm information."},
{"role": "user", "content": specification}
],
response_format=FarmActivities,
)
if 'error' in farm_info_response:
raise ValueError(f"API error: {interactions_response['error']['message']}")
farm_generated_json = farm_info_response.choices[0].message.parsed
print("FARM JSON: ")
print(farm_generated_json) # debugging
farm_pretty_json = farm_generated_json.json()
interactions_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the interactions information."},
{"role": "user", "content": specification}
],
response_format=Interactions,
)
if 'error' in interactions_response:
raise ValueError(f"API error: {interactions_response['error']['message']}")
interactions_generated_json = interactions_response.choices[0].message.parsed
print("INTERACTIONS JSON: ")
print(interactions_generated_json) # debugging 2
interactions_pretty_json = interactions_generated_json.json()
trial_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the trial information."},
{"role": "user", "content": specification}
],
response_format=Trial,
)
if 'error' in trial_response:
raise ValueError(f"API error: {trial_response['error']['message']}")
trial_generated_json = trial_response.choices[0].message.parsed
print("TRIALS JSON: ")
print(trial_generated_json) # debugging 3
trial_pretty_json = trial_generated_json.json()
return farm_pretty_json, interactions_pretty_json, trial_pretty_json
except ValidationError as e:
return {"error": str(e)}
except Exception as e:
return {"error": "Failed to generate valid JSON. " + str(e)}
# This is for the step-wise JSON creation
def generate_json_pieces(specification, model_version, additional_json_creation_options, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input):
if additional_json_creation_options == "Explicit specific pieces":
field_data_specification = field_data_input
planting_data_specification = planting_data_input
logs_data_specification = logs_data_input
soil_data_specification = soil_data_input
yield_data_specification = yield_data_input
elif additional_json_creation_options == "Parse from one big input text":
field_data_specification = specification
planting_data_specification = specification
logs_data_specification = specification
soil_data_specification = specification
yield_data_specification = specification
try:
# Call OpenAI API to generate structured output based on prompt
field_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the field information."},
{"role": "user", "content": field_data_specification}
],
response_format=FarmActivitiesLite,
)
plant_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the planting information."},
{"role": "user", "content": planting_data_specification}
],
response_format=PlantingLite,
)
log_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the planting information."},
{"role": "user", "content": logs_data_specification}
],
response_format=Log,
)
soil_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the planting information."},
{"role": "user", "content": soil_data_specification}
],
response_format=Soil,
)
yield_response = client.beta.chat.completions.parse(
model=model_version, # Use GPT model that supports structured output
messages=[
{"role": "system", "content": "Extract the planting information."},
{"role": "user", "content": yield_data_specification}
],
response_format=Yield,
)
combined_json = field_response.choices[0].message.parsed.copy()
combined_json["plantings"] = plant_response.choices[0].message.parsed
combined_json["plantings"]["logs"] = log_response.choices[0].message.parsed
combined_json["plantings"]["soil"] = soil_response.choices[0].message.parsed
combined_json["plantings"]["yield"] = yield_response.choices[0].message.parsed
print(combined_json) # debugging
pretty_json = combined_json.json()
if 'error' in response:
raise ValueError(f"API error: {response['error']['message']}")
return pretty_json
except ValidationError as e:
return {"error": str(e)}
except Exception as e:
return {"error": "Failed to generate valid JSON. " + str(e)}
def process_specifications(data, model_version, json_creation, additional_json_creation_options, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input):
# This method just drives the process
# Uncomment when working on flippers
#if json_creation == "Single JSON Creation":
# resulting_schema = generate_json(data, model_version)
#elif json_creation == "Step-wise JSON Creation":
# resulting_schema = generate_json_pieces(data, model_version, additional_json_creation_options, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input)
#return resulting_schema
global original_outputs, xml_outputs
output1, output2, output3 = generate_json(data, model_version)
original_outputs = [output1, output2, output3]
xml_outputs = []
return output1, output2, output3, Toggle(visible=True)
with gr.Blocks() as demo:
data_input = gr.Textbox(label="Enter your data", placeholder="Type your data here")
model_version_input = gr.Radio(["gpt-4o-2024-08-06", "gpt-4o-mini-2024-07-18"], label="Model Versions")
# Hidden for demo purposes
json_creation_input = gr.Radio(["Single JSON Creation", "Step-wise JSON Creation"], label="Modularity of JSON Approach", visible=False)
additional_json_creation_options = gr.Radio(["Parse from one big input text", "Explicit specific pieces"], label="Additional Step-wise JSON Options", visible=False)
# Explicit Specific Pieces
field_data_input = gr.Textbox(label="Enter your data for field", placeholder="Field Name and Description", visible=False)
planting_data_input = gr.Textbox(label="Enter your data for plantings", placeholder="Name, Status (active/archived), Crop, Crop variety", visible=False)
logs_data_input = gr.Textbox(label="Enter your log data", placeholder="Convention, Date, Description", visible=False)
soil_data_input = gr.Textbox(label="Enter your soil data", placeholder="Description, Structure, Biology", visible=False)
yield_data_input = gr.Textbox(label="Enter your yield data", placeholder="Quantity, Quality", visible=False)
with gr.Row():
farm_output_box = gr.Textbox(label="Fields and Activities Output Data", interactive=False)
interactions_output_box = gr.Textbox(label="Interactions Output Data", interactive=False)
trials_output_box = gr.Textbox(label="Trials Output Data", interactive=False, info="Treatment learnings, variables (control, independent and outcome), and confounding factors are currently NOT included (as they break everything)")
def update_visibility(radio, additional_options):
value = radio
if value == "Single JSON Creation":
return [gr.Radio(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0))]
elif value == "Step-wise JSON Creation" and (additional_options == None or additional_options == "Parse from one big input text"):
return [gr.Radio(visible=bool(1)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0))]
else:
return [gr.Radio(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1))]
def update_visibility2(radio):
value = radio
if value == "Explicit specific pieces":
return [gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(1))]
else:
return [gr.Textbox(visible=bool(1)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0)), gr.Textbox(visible=bool(0))]
def update_toggle(toggle, farm_output_box, interactions_output_box, trials_output_box):
global original_outputs, xml_outputs
if toggle and not xml_outputs:
farm_dict = json.loads(farm_output_box)
interactions_dict = json.loads(interactions_output_box)
trials_dict = json.loads(trials_output_box)
farm_xml = dicttoxml(farm_dict)
interactions_xml = dicttoxml(interactions_dict)
trials_xml = dicttoxml(trials_dict)
xml_outputs = [farm_xml, interactions_xml, trials_xml]
return farm_xml, interactions_xml, trials_xml
elif toggle and xml_outputs:
return xml_outputs[0], xml_outputs[1], xml_outputs[2]
else:
return original_outputs[0], original_outputs[1], original_outputs[2]
json_creation_input.change(fn=update_visibility, inputs=[json_creation_input, additional_json_creation_options], outputs=[additional_json_creation_options, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input])
additional_json_creation_options.change(fn=update_visibility2, inputs=[additional_json_creation_options], outputs=[data_input, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input])
toggle_output = Toggle(label="JSON <-> XML", value=False, info="Toggle Output Data", interactive=True, visible=False)
submit_button = gr.Button("Generate JSON")
submit_button.click(
fn=process_specifications,
inputs=[data_input, model_version_input, json_creation_input, additional_json_creation_options, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input],
outputs=[farm_output_box, interactions_output_box, trials_output_box, toggle_output]
)
clear_button = gr.ClearButton(components=[data_input, model_version_input, json_creation_input, additional_json_creation_options, field_data_input, planting_data_input, logs_data_input, soil_data_input, yield_data_input])
toggle_output.change(fn=update_toggle, inputs=[toggle_output, farm_output_box, interactions_output_box, trials_output_box], outputs=[farm_output_box, interactions_output_box, trials_output_box])
if __name__ == "__main__":
demo.launch() |