Simon Strandgaard commited on
Commit
f0808c8
·
1 Parent(s): 767b265

Snapshot of PlanExe commit 5012c8a07355571673d80b3e1f60b5fc0fc1a715

Browse files
src/plan/data/simple_plan_prompts.jsonl CHANGED
@@ -23,3 +23,13 @@
23
  {"id": "676cbca8-5d49-42a0-8826-398318004703", "prompt": "Write a Python script for a snake shape keep bouncing within a pentagon. Make sure to handle collision detection properly. Make the pentagon slowly rotate.", "tags": ["programming", "python", "collision detection"]}
24
  {"id": "a9113924-6148-4a0c-b72a-eecdb856e1e2", "prompt": "Investigate outbreak of a deadly new disease in the jungle.", "tags": ["outbreak", "jungle"]}
25
  {"id": "4dc34d55-0d0d-4e9d-92f4-23765f49dd29", "prompt": "Establish a solar farm in Denmark.", "tags": ["denmark", "energy", "sun"]}
 
 
 
 
 
 
 
 
 
 
 
23
  {"id": "676cbca8-5d49-42a0-8826-398318004703", "prompt": "Write a Python script for a snake shape keep bouncing within a pentagon. Make sure to handle collision detection properly. Make the pentagon slowly rotate.", "tags": ["programming", "python", "collision detection"]}
24
  {"id": "a9113924-6148-4a0c-b72a-eecdb856e1e2", "prompt": "Investigate outbreak of a deadly new disease in the jungle.", "tags": ["outbreak", "jungle"]}
25
  {"id": "4dc34d55-0d0d-4e9d-92f4-23765f49dd29", "prompt": "Establish a solar farm in Denmark.", "tags": ["denmark", "energy", "sun"]}
26
+ {"id": "0bb4a7d3-c16b-4b21-8a9b-20e1cd4002d4", "prompt": "Develop a sustainable solution for extreme poverty in regions where people live on less than 2 USD per day. Focus on improving access to basic necessities like food, shelter, and clean water, and explain how you would allocate investments given that sectors like infrastructure may require higher per capita funding. Indicate whether your approach leverages existing systems or builds new capacity. Assume this initiative will impact 5 million people over 5 years with a total budget of 500 million USD.", "tags": ["poverty", "sustainability", "development"]}
27
+ {"id": "307f7e0c-a160-4b7a-9e3c-76577164497e", "prompt": "Create a comprehensive plan to address hunger and malnutrition in impoverished communities by enhancing food security and nutritional education. Provide a detailed cost breakdown and clarify which components might need additional per capita investment. State whether you will leverage existing food distribution networks or develop new infrastructure. Assume the plan targets 3 million individuals across 10 countries with a total budget of 300 million USD over 4 years.", "tags": ["hunger", "malnutrition", "nutrition"]}
28
+ {"id": "1ea5076a-95b5-47fe-aff5-f03c8ef00cd7", "prompt": "Implement a comprehensive strategy for providing clean water to areas where it is currently unavailable. Your solution should include infrastructure development for water purification, distribution, and ongoing maintenance, and clarify if you are upgrading existing systems or building new ones from scratch. Assume this project will serve 2 million people in rural areas over 3 years with a total budget of 200 million USD.", "tags": ["water", "sanitation", "infrastructure"]}
29
+ {"id": "08505146-b6ad-4d7c-b6b6-85c2868e707d", "prompt": "Design an initiative to improve sanitation in both slums and rural areas to reduce disease spread and enhance living conditions. Provide a detailed allocation plan that accounts for the varying needs of urban versus rural contexts, and note that phased investments may be necessary for sustainable infrastructure. Assume the initiative will reach 1.5 million people in 50 different locations over 5 years with a total budget of 150 million USD.", "tags": ["sanitation", "health", "slums"]}
30
+ {"id": "0bb00fe6-711c-4612-8f83-a9a88e5c7958", "prompt": "Establish accessible healthcare systems in regions where medical facilities are scarce, focusing on both preventive care and emergency services. Clarify whether your approach involves upgrading existing facilities or constructing new ones, and account for the typically higher per capita costs in healthcare infrastructure. Assume the program will cover 4 million people in 20 countries over 6 years with a total budget of 400 million USD.", "tags": ["healthcare", "accessibility", "medicine"]}
31
+ {"id": "79b62957-cae7-4727-8450-4f5f29d4ddda", "prompt": "Create a detailed plan to increase educational opportunities in poverty-stricken areas. This plan should include building or improving schools, teacher training, and scholarship programs, with a clear breakdown of resource allocation among these components. Recognize that educational initiatives may require higher per capita investments for infrastructure compared to service-based interventions. Assume the plan will support 1 million students over 7 years with a total budget of 350 million USD.", "tags": ["education", "literacy", "development"]}
32
+ {"id": "6f7a8b9c-0d1e-2f3a-4b5c-6d7e8f90a1b2", "prompt": "Develop comprehensive policies and programs to eradicate child labor by ensuring children have access to education and protection from exploitation. Include details on scaling the initiative and allocating funds among enforcement, educational support, and social services. Assume this effort will benefit 500,000 children in 15 countries over 5 years with a total budget of 100 million USD.", "tags": ["child labor", "education", "protection"]}
33
+ {"id": "a9f410c0-120e-45d6-b042-e88ca47b39bb", "prompt": "Formulate a housing security plan that addresses overcrowding, unsafe living conditions, and homelessness by promoting secure and dignified housing. Clarify whether the initiative involves new construction, rehabilitation of existing structures, or a combination of both, and outline a phased investment strategy given the higher costs typically associated with housing projects. Assume the plan will provide housing for 200,000 individuals over 4 years with a total budget of 250 million USD.", "tags": ["housing", "security", "urban development"]}
34
+ {"id": "cdf7f29d-bbcb-478d-8b5a-e82e74ed8626", "prompt": "Propose comprehensive peace initiatives and conflict resolution strategies for areas experiencing high rates of violence and political instability. Detail how your approach will protect vulnerable populations, including mechanisms for community engagement, reconciliation, and rebuilding. Assume the intervention will affect 1 million people in conflict zones over 3 years with a total budget of 150 million USD.", "tags": ["conflict", "peace", "stability"]}
35
+ {"id": "79ef9ebf-3173-4b33-81f9-abbd3da7da6d", "prompt": "Design robust adaptation and resilience programs for communities facing environmental degradation and the effects of climate change, especially in disaster-prone regions. Include both short-term relief measures and long-term sustainability strategies, and provide details on how funds will be allocated between immediate response and infrastructure improvements. Assume the initiative will aid 1.2 million people in the 10 most affected countries over 5 years with a total budget of 180 million USD.", "tags": ["environment", "climate change", "resilience"]}
src/plan/filenames.py CHANGED
@@ -8,26 +8,36 @@ class FilenameEnum(str, Enum):
8
  PRE_PROJECT_ASSESSMENT_RAW = "004-1-pre_project_assessment_raw.json"
9
  PRE_PROJECT_ASSESSMENT = "004-2-pre_project_assessment.json"
10
  PROJECT_PLAN = "005-project_plan.json"
11
- SWOT_RAW = "006-1-swot_analysis_raw.json"
12
- SWOT_MARKDOWN = "006-2-swot_analysis.md"
13
- EXPERTS_RAW = "007-1-experts_raw.json"
14
- EXPERTS_CLEAN = "007-2-experts.json"
15
- EXPERT_CRITICISM_RAW_TEMPLATE = "008-1-{}-expert_criticism_raw.json"
16
- EXPERT_CRITICISM_MARKDOWN = "008-2-expert_criticism.md"
17
- WBS_LEVEL1_RAW = "009-1-wbs_level1_raw.json"
18
- WBS_LEVEL1 = "009-2-wbs_level1.json"
19
- WBS_LEVEL2_RAW = "010-1-wbs_level2_raw.json"
20
- WBS_LEVEL2 = "010-2-wbs_level2.json"
21
- WBS_PROJECT_LEVEL1_AND_LEVEL2 = "011-wbs_project_level1_and_level2.json"
22
- PITCH_RAW = "012-1-pitch_raw.json"
23
- PITCH_CONVERT_TO_MARKDOWN_RAW = "012-2-pitch_to_markdown_raw.json"
24
- PITCH_MARKDOWN = "012-3-pitch.md"
25
- TASK_DEPENDENCIES_RAW = "013-task_dependencies_raw.json"
26
- TASK_DURATIONS_RAW_TEMPLATE = "014-1-{}-task_durations_raw.json"
27
- TASK_DURATIONS = "014-2-task_durations.json"
28
- WBS_LEVEL3_RAW_TEMPLATE = "015-1-{}-wbs_level3_raw.json"
29
- WBS_LEVEL3 = "015-2-wbs_level3.json"
30
- WBS_PROJECT_LEVEL1_AND_LEVEL2_AND_LEVEL3_FULL = "015-3-wbs_project_level1_and_level2_and_level3.json"
31
- WBS_PROJECT_LEVEL1_AND_LEVEL2_AND_LEVEL3_CSV = "015-4-wbs_project_level1_and_level2_and_level3.csv"
32
- REPORT = "016-report.html"
 
 
 
 
 
 
 
 
 
 
33
  PIPELINE_COMPLETE = "999-pipeline_complete.txt"
 
8
  PRE_PROJECT_ASSESSMENT_RAW = "004-1-pre_project_assessment_raw.json"
9
  PRE_PROJECT_ASSESSMENT = "004-2-pre_project_assessment.json"
10
  PROJECT_PLAN = "005-project_plan.json"
11
+ FIND_TEAM_MEMBERS_RAW = "006-1-find_team_members_raw.json"
12
+ FIND_TEAM_MEMBERS_CLEAN = "006-2-find_team_members.json"
13
+ ENRICH_TEAM_MEMBERS_CONTRACT_TYPE_RAW = "007-1-enrich_team_members_contract_type_raw.json"
14
+ ENRICH_TEAM_MEMBERS_CONTRACT_TYPE_CLEAN = "007-2-enrich_team_members_contract_type.json"
15
+ ENRICH_TEAM_MEMBERS_BACKGROUND_STORY_RAW = "008-1-enrich_team_members_background_story_raw.json"
16
+ ENRICH_TEAM_MEMBERS_BACKGROUND_STORY_CLEAN = "008-2-enrich_team_members_background_story.json"
17
+ ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_RAW = "009-1-enrich_team_members_environment_info_raw.json"
18
+ ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_CLEAN = "009-2-enrich_team_members_environment_info.json"
19
+ REVIEW_TEAM_RAW = "010-review_team_raw.json"
20
+ TEAM_MARKDOWN = "011-team.md"
21
+ SWOT_RAW = "012-1-swot_analysis_raw.json"
22
+ SWOT_MARKDOWN = "012-2-swot_analysis.md"
23
+ EXPERTS_RAW = "013-1-experts_raw.json"
24
+ EXPERTS_CLEAN = "013-2-experts.json"
25
+ EXPERT_CRITICISM_RAW_TEMPLATE = "014-1-{}-expert_criticism_raw.json"
26
+ EXPERT_CRITICISM_MARKDOWN = "014-2-expert_criticism.md"
27
+ WBS_LEVEL1_RAW = "015-1-wbs_level1_raw.json"
28
+ WBS_LEVEL1 = "015-2-wbs_level1.json"
29
+ WBS_LEVEL2_RAW = "016-1-wbs_level2_raw.json"
30
+ WBS_LEVEL2 = "016-2-wbs_level2.json"
31
+ WBS_PROJECT_LEVEL1_AND_LEVEL2 = "017-wbs_project_level1_and_level2.json"
32
+ PITCH_RAW = "018-1-pitch_raw.json"
33
+ PITCH_CONVERT_TO_MARKDOWN_RAW = "018-2-pitch_to_markdown_raw.json"
34
+ PITCH_MARKDOWN = "018-3-pitch.md"
35
+ TASK_DEPENDENCIES_RAW = "019-task_dependencies_raw.json"
36
+ TASK_DURATIONS_RAW_TEMPLATE = "020-1-{}-task_durations_raw.json"
37
+ TASK_DURATIONS = "020-2-task_durations.json"
38
+ WBS_LEVEL3_RAW_TEMPLATE = "021-1-{}-wbs_level3_raw.json"
39
+ WBS_LEVEL3 = "021-2-wbs_level3.json"
40
+ WBS_PROJECT_LEVEL1_AND_LEVEL2_AND_LEVEL3_FULL = "021-3-wbs_project_level1_and_level2_and_level3.json"
41
+ WBS_PROJECT_LEVEL1_AND_LEVEL2_AND_LEVEL3_CSV = "021-4-wbs_project_level1_and_level2_and_level3.csv"
42
+ REPORT = "022-report.html"
43
  PIPELINE_COMPLETE = "999-pipeline_complete.txt"
src/plan/run_plan_pipeline.py CHANGED
@@ -31,6 +31,12 @@ from src.pitch.create_pitch import CreatePitch
31
  from src.pitch.convert_pitch_to_markdown import ConvertPitchToMarkdown
32
  from src.plan.identify_wbs_task_dependencies import IdentifyWBSTaskDependencies
33
  from src.plan.estimate_wbs_task_durations import EstimateWBSTaskDurations
 
 
 
 
 
 
34
  from src.wbs.wbs_task import WBSTask, WBSProject
35
  from src.wbs.wbs_populate import WBSPopulate
36
  from src.llm_factory import get_llm
@@ -210,6 +216,401 @@ class ProjectPlanTask(PlanTask):
210
  logger.info("Project plan created and saved to %s", output_path)
211
 
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  class SWOTAnalysisTask(PlanTask):
214
  llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
215
 
@@ -888,6 +1289,7 @@ class ReportTask(PlanTask):
888
 
889
  def requires(self):
890
  return {
 
891
  'swot_analysis': SWOTAnalysisTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
892
  'pitch_markdown': ConvertPitchToMarkdownTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
893
  'wbs_project123': WBSProjectLevel1AndLevel2AndLevel3Task(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
@@ -898,6 +1300,7 @@ class ReportTask(PlanTask):
898
  rg = ReportGenerator()
899
  rg.append_pitch_markdown(self.input()['pitch_markdown']['markdown'].path)
900
  rg.append_swot_analysis_markdown(self.input()['swot_analysis']['markdown'].path)
 
901
  rg.append_project_plan_csv(self.input()['wbs_project123']['csv'].path)
902
  rg.append_expert_criticism_markdown(self.input()['expert_review'].path)
903
  rg.save_report(self.output().path)
@@ -911,6 +1314,12 @@ class FullPlanPipeline(PlanTask):
911
  'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
912
  'pre_project_assessment': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
913
  'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
 
 
 
 
 
 
914
  'swot_analysis': SWOTAnalysisTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
915
  'expert_review': ExpertReviewTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
916
  'wbs_level1': CreateWBSLevel1Task(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
 
31
  from src.pitch.convert_pitch_to_markdown import ConvertPitchToMarkdown
32
  from src.plan.identify_wbs_task_dependencies import IdentifyWBSTaskDependencies
33
  from src.plan.estimate_wbs_task_durations import EstimateWBSTaskDurations
34
+ from src.team.find_team_members import FindTeamMembers
35
+ from src.team.enrich_team_members_with_contract_type import EnrichTeamMembersWithContractType
36
+ from src.team.enrich_team_members_with_background_story import EnrichTeamMembersWithBackgroundStory
37
+ from src.team.enrich_team_members_with_environment_info import EnrichTeamMembersWithEnvironmentInfo
38
+ from src.team.team_markdown_document import TeamMarkdownDocumentBuilder
39
+ from src.team.review_team import ReviewTeam
40
  from src.wbs.wbs_task import WBSTask, WBSProject
41
  from src.wbs.wbs_populate import WBSPopulate
42
  from src.llm_factory import get_llm
 
216
  logger.info("Project plan created and saved to %s", output_path)
217
 
218
 
219
+ class FindTeamMembersTask(PlanTask):
220
+ llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
221
+
222
+ def requires(self):
223
+ return {
224
+ 'setup': SetupTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail),
225
+ 'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
226
+ 'preproject': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
227
+ 'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model)
228
+ }
229
+
230
+ def output(self):
231
+ return {
232
+ 'raw': luigi.LocalTarget(str(self.file_path(FilenameEnum.FIND_TEAM_MEMBERS_RAW))),
233
+ 'clean': luigi.LocalTarget(str(self.file_path(FilenameEnum.FIND_TEAM_MEMBERS_CLEAN)))
234
+ }
235
+
236
+ def run(self):
237
+ logger.info("FindTeamMembers. Loading files...")
238
+
239
+ # 1. Read the plan prompt from SetupTask.
240
+ with self.input()['setup'].open("r") as f:
241
+ plan_prompt = f.read()
242
+
243
+ # 2. Read the distilled assumptions from AssumptionsTask.
244
+ with self.input()['assumptions'].open("r") as f:
245
+ assumption_list = json.load(f)
246
+
247
+ # 3. Read the pre-project assessment from PreProjectAssessmentTask.
248
+ with self.input()['preproject']['clean'].open("r") as f:
249
+ pre_project_assessment_dict = json.load(f)
250
+
251
+ # 4. Read the project plan from ProjectPlanTask.
252
+ with self.input()['project_plan'].open("r") as f:
253
+ project_plan_dict = json.load(f)
254
+
255
+ logger.info("FindTeamMembers. All files are now ready. Brainstorming a team...")
256
+
257
+ # Build the query.
258
+ query = (
259
+ f"Initial plan: {plan_prompt}\n\n"
260
+ f"Assumptions:\n{format_json_for_use_in_query(assumption_list)}\n\n"
261
+ f"Pre-project assessment:\n{format_json_for_use_in_query(pre_project_assessment_dict)}\n\n"
262
+ f"Project plan:\n{format_json_for_use_in_query(project_plan_dict)}"
263
+ )
264
+
265
+ # Create LLM instance.
266
+ llm = get_llm(self.llm_model)
267
+
268
+ # Execute.
269
+ try:
270
+ find_team_members = FindTeamMembers.execute(llm, query)
271
+ except Exception as e:
272
+ logger.error("FindTeamMembers failed: %s", e)
273
+ raise
274
+
275
+ # Save the raw output.
276
+ raw_dict = find_team_members.to_dict()
277
+ with self.output()['raw'].open("w") as f:
278
+ json.dump(raw_dict, f, indent=2)
279
+
280
+ # Save the cleaned up result.
281
+ team_member_list = find_team_members.team_member_list
282
+ with self.output()['clean'].open("w") as f:
283
+ json.dump(team_member_list, f, indent=2)
284
+
285
+ logger.info("FindTeamMembers complete.")
286
+
287
+ class EnrichTeamMembersWithContractTypeTask(PlanTask):
288
+ llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
289
+
290
+ def requires(self):
291
+ return {
292
+ 'setup': SetupTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail),
293
+ 'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
294
+ 'preproject': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
295
+ 'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
296
+ 'find_team_members': FindTeamMembersTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model)
297
+ }
298
+
299
+ def output(self):
300
+ return {
301
+ 'raw': luigi.LocalTarget(str(self.file_path(FilenameEnum.ENRICH_TEAM_MEMBERS_CONTRACT_TYPE_RAW))),
302
+ 'clean': luigi.LocalTarget(str(self.file_path(FilenameEnum.ENRICH_TEAM_MEMBERS_CONTRACT_TYPE_CLEAN)))
303
+ }
304
+
305
+ def run(self):
306
+ logger.info("EnrichTeamMembersWithContractType. Loading files...")
307
+
308
+ # 1. Read the plan prompt from SetupTask.
309
+ with self.input()['setup'].open("r") as f:
310
+ plan_prompt = f.read()
311
+
312
+ # 2. Read the distilled assumptions from AssumptionsTask.
313
+ with self.input()['assumptions'].open("r") as f:
314
+ assumption_list = json.load(f)
315
+
316
+ # 3. Read the pre-project assessment from PreProjectAssessmentTask.
317
+ with self.input()['preproject']['clean'].open("r") as f:
318
+ pre_project_assessment_dict = json.load(f)
319
+
320
+ # 4. Read the project plan from ProjectPlanTask.
321
+ with self.input()['project_plan'].open("r") as f:
322
+ project_plan_dict = json.load(f)
323
+
324
+ # 5. Read the team_member_list from FindTeamMembersTask.
325
+ with self.input()['find_team_members']['clean'].open("r") as f:
326
+ team_member_list = json.load(f)
327
+
328
+ logger.info("EnrichTeamMembersWithContractType. All files are now ready. Processing...")
329
+
330
+ # Build the query.
331
+ query = (
332
+ f"Initial plan: {plan_prompt}\n\n"
333
+ f"Assumptions:\n{format_json_for_use_in_query(assumption_list)}\n\n"
334
+ f"Pre-project assessment:\n{format_json_for_use_in_query(pre_project_assessment_dict)}\n\n"
335
+ f"Project plan:\n{format_json_for_use_in_query(project_plan_dict)}\n\n"
336
+ f"Here is the list of team members that needs to be enriched:\n{format_json_for_use_in_query(team_member_list)}"
337
+ )
338
+
339
+ # Create LLM instance.
340
+ llm = get_llm(self.llm_model)
341
+
342
+ # Execute.
343
+ try:
344
+ enrich_team_members_with_contract_type = EnrichTeamMembersWithContractType.execute(llm, query, team_member_list)
345
+ except Exception as e:
346
+ logger.error("EnrichTeamMembersWithContractType failed: %s", e)
347
+ raise
348
+
349
+ # Save the raw output.
350
+ raw_dict = enrich_team_members_with_contract_type.to_dict()
351
+ with self.output()['raw'].open("w") as f:
352
+ json.dump(raw_dict, f, indent=2)
353
+
354
+ # Save the cleaned up result.
355
+ team_member_list = enrich_team_members_with_contract_type.team_member_list
356
+ with self.output()['clean'].open("w") as f:
357
+ json.dump(team_member_list, f, indent=2)
358
+
359
+ logger.info("EnrichTeamMembersWithContractType complete.")
360
+
361
+ class EnrichTeamMembersWithBackgroundStoryTask(PlanTask):
362
+ llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
363
+
364
+ def requires(self):
365
+ return {
366
+ 'setup': SetupTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail),
367
+ 'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
368
+ 'preproject': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
369
+ 'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
370
+ 'enrich_team_members_with_contract_type': EnrichTeamMembersWithContractTypeTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model)
371
+ }
372
+
373
+ def output(self):
374
+ return {
375
+ 'raw': luigi.LocalTarget(str(self.file_path(FilenameEnum.ENRICH_TEAM_MEMBERS_BACKGROUND_STORY_RAW))),
376
+ 'clean': luigi.LocalTarget(str(self.file_path(FilenameEnum.ENRICH_TEAM_MEMBERS_BACKGROUND_STORY_CLEAN)))
377
+ }
378
+
379
+ def run(self):
380
+ logger.info("EnrichTeamMembersWithBackgroundStoryTask. Loading files...")
381
+
382
+ # 1. Read the plan prompt from SetupTask.
383
+ with self.input()['setup'].open("r") as f:
384
+ plan_prompt = f.read()
385
+
386
+ # 2. Read the distilled assumptions from AssumptionsTask.
387
+ with self.input()['assumptions'].open("r") as f:
388
+ assumption_list = json.load(f)
389
+
390
+ # 3. Read the pre-project assessment from PreProjectAssessmentTask.
391
+ with self.input()['preproject']['clean'].open("r") as f:
392
+ pre_project_assessment_dict = json.load(f)
393
+
394
+ # 4. Read the project plan from ProjectPlanTask.
395
+ with self.input()['project_plan'].open("r") as f:
396
+ project_plan_dict = json.load(f)
397
+
398
+ # 5. Read the team_member_list from EnrichTeamMembersWithContractTypeTask.
399
+ with self.input()['enrich_team_members_with_contract_type']['clean'].open("r") as f:
400
+ team_member_list = json.load(f)
401
+
402
+ logger.info("EnrichTeamMembersWithBackgroundStoryTask. All files are now ready. Processing...")
403
+
404
+ # Build the query.
405
+ query = (
406
+ f"Initial plan: {plan_prompt}\n\n"
407
+ f"Assumptions:\n{format_json_for_use_in_query(assumption_list)}\n\n"
408
+ f"Pre-project assessment:\n{format_json_for_use_in_query(pre_project_assessment_dict)}\n\n"
409
+ f"Project plan:\n{format_json_for_use_in_query(project_plan_dict)}\n\n"
410
+ f"Here is the list of team members that needs to be enriched:\n{format_json_for_use_in_query(team_member_list)}"
411
+ )
412
+
413
+ # Create LLM instance.
414
+ llm = get_llm(self.llm_model)
415
+
416
+ # Execute.
417
+ try:
418
+ enrich_team_members_with_background_story = EnrichTeamMembersWithBackgroundStory.execute(llm, query, team_member_list)
419
+ except Exception as e:
420
+ logger.error("EnrichTeamMembersWithBackgroundStory failed: %s", e)
421
+ raise
422
+
423
+ # Save the raw output.
424
+ raw_dict = enrich_team_members_with_background_story.to_dict()
425
+ with self.output()['raw'].open("w") as f:
426
+ json.dump(raw_dict, f, indent=2)
427
+
428
+ # Save the cleaned up result.
429
+ team_member_list = enrich_team_members_with_background_story.team_member_list
430
+ with self.output()['clean'].open("w") as f:
431
+ json.dump(team_member_list, f, indent=2)
432
+
433
+ logger.info("EnrichTeamMembersWithBackgroundStoryTask complete.")
434
+
435
+ class EnrichTeamMembersWithEnvironmentInfoTask(PlanTask):
436
+ llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
437
+
438
+ def requires(self):
439
+ return {
440
+ 'setup': SetupTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail),
441
+ 'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
442
+ 'preproject': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
443
+ 'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
444
+ 'enrich_team_members_with_background_story': EnrichTeamMembersWithBackgroundStoryTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model)
445
+ }
446
+
447
+ def output(self):
448
+ return {
449
+ 'raw': luigi.LocalTarget(str(self.file_path(FilenameEnum.ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_RAW))),
450
+ 'clean': luigi.LocalTarget(str(self.file_path(FilenameEnum.ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_CLEAN)))
451
+ }
452
+
453
+ def run(self):
454
+ logger.info("EnrichTeamMembersWithEnvironmentInfoTask. Loading files...")
455
+
456
+ # 1. Read the plan prompt from SetupTask.
457
+ with self.input()['setup'].open("r") as f:
458
+ plan_prompt = f.read()
459
+
460
+ # 2. Read the distilled assumptions from AssumptionsTask.
461
+ with self.input()['assumptions'].open("r") as f:
462
+ assumption_list = json.load(f)
463
+
464
+ # 3. Read the pre-project assessment from PreProjectAssessmentTask.
465
+ with self.input()['preproject']['clean'].open("r") as f:
466
+ pre_project_assessment_dict = json.load(f)
467
+
468
+ # 4. Read the project plan from ProjectPlanTask.
469
+ with self.input()['project_plan'].open("r") as f:
470
+ project_plan_dict = json.load(f)
471
+
472
+ # 5. Read the team_member_list from EnrichTeamMembersWithBackgroundStoryTask.
473
+ with self.input()['enrich_team_members_with_background_story']['clean'].open("r") as f:
474
+ team_member_list = json.load(f)
475
+
476
+ logger.info("EnrichTeamMembersWithEnvironmentInfoTask. All files are now ready. Processing...")
477
+
478
+ # Build the query.
479
+ query = (
480
+ f"Initial plan: {plan_prompt}\n\n"
481
+ f"Assumptions:\n{format_json_for_use_in_query(assumption_list)}\n\n"
482
+ f"Pre-project assessment:\n{format_json_for_use_in_query(pre_project_assessment_dict)}\n\n"
483
+ f"Project plan:\n{format_json_for_use_in_query(project_plan_dict)}\n\n"
484
+ f"Here is the list of team members that needs to be enriched:\n{format_json_for_use_in_query(team_member_list)}"
485
+ )
486
+
487
+ # Create LLM instance.
488
+ llm = get_llm(self.llm_model)
489
+
490
+ # Execute.
491
+ try:
492
+ enrich_team_members_with_background_story = EnrichTeamMembersWithEnvironmentInfo.execute(llm, query, team_member_list)
493
+ except Exception as e:
494
+ logger.error("EnrichTeamMembersWithEnvironmentInfo failed: %s", e)
495
+ raise
496
+
497
+ # Save the raw output.
498
+ raw_dict = enrich_team_members_with_background_story.to_dict()
499
+ with self.output()['raw'].open("w") as f:
500
+ json.dump(raw_dict, f, indent=2)
501
+
502
+ # Save the cleaned up result.
503
+ team_member_list = enrich_team_members_with_background_story.team_member_list
504
+ with self.output()['clean'].open("w") as f:
505
+ json.dump(team_member_list, f, indent=2)
506
+
507
+ logger.info("EnrichTeamMembersWithEnvironmentInfoTask complete.")
508
+
509
+ class ReviewTeamTask(PlanTask):
510
+ llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
511
+
512
+ def requires(self):
513
+ return {
514
+ 'setup': SetupTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail),
515
+ 'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
516
+ 'preproject': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
517
+ 'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
518
+ 'enrich_team_members_with_environment_info': EnrichTeamMembersWithEnvironmentInfoTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model)
519
+ }
520
+
521
+ def output(self):
522
+ return luigi.LocalTarget(str(self.file_path(FilenameEnum.REVIEW_TEAM_RAW)))
523
+
524
+ def run(self):
525
+ logger.info("ReviewTeamTask. Loading files...")
526
+
527
+ # 1. Read the plan prompt from SetupTask.
528
+ with self.input()['setup'].open("r") as f:
529
+ plan_prompt = f.read()
530
+
531
+ # 2. Read the distilled assumptions from AssumptionsTask.
532
+ with self.input()['assumptions'].open("r") as f:
533
+ assumption_list = json.load(f)
534
+
535
+ # 3. Read the pre-project assessment from PreProjectAssessmentTask.
536
+ with self.input()['preproject']['clean'].open("r") as f:
537
+ pre_project_assessment_dict = json.load(f)
538
+
539
+ # 4. Read the project plan from ProjectPlanTask.
540
+ with self.input()['project_plan'].open("r") as f:
541
+ project_plan_dict = json.load(f)
542
+
543
+ # 5. Read the team_member_list from EnrichTeamMembersWithEnvironmentInfoTask.
544
+ with self.input()['enrich_team_members_with_environment_info']['clean'].open("r") as f:
545
+ team_member_list = json.load(f)
546
+
547
+ logger.info("ReviewTeamTask. All files are now ready. Processing...")
548
+
549
+ # Convert the team members to a Markdown document.
550
+ builder = TeamMarkdownDocumentBuilder()
551
+ builder.append_roles(team_member_list, title=None)
552
+ team_document_markdown = builder.to_string()
553
+
554
+ # Build the query.
555
+ query = (
556
+ f"Initial plan: {plan_prompt}\n\n"
557
+ f"Assumptions:\n{format_json_for_use_in_query(assumption_list)}\n\n"
558
+ f"Pre-project assessment:\n{format_json_for_use_in_query(pre_project_assessment_dict)}\n\n"
559
+ f"Project plan:\n{format_json_for_use_in_query(project_plan_dict)}\n\n"
560
+ f"Document with team members:\n{team_document_markdown}"
561
+ )
562
+
563
+ # Create LLM instance.
564
+ llm = get_llm(self.llm_model)
565
+
566
+ # Execute.
567
+ try:
568
+ review_team = ReviewTeam.execute(llm, query)
569
+ except Exception as e:
570
+ logger.error("ReviewTeam failed: %s", e)
571
+ raise
572
+
573
+ # Save the raw output.
574
+ raw_dict = review_team.to_dict()
575
+ with self.output().open("w") as f:
576
+ json.dump(raw_dict, f, indent=2)
577
+
578
+ logger.info("ReviewTeamTask complete.")
579
+
580
+ class TeamMarkdownTask(PlanTask):
581
+ llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
582
+
583
+ def requires(self):
584
+ return {
585
+ 'enrich_team_members_with_environment_info': EnrichTeamMembersWithEnvironmentInfoTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
586
+ 'review_team': ReviewTeamTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model)
587
+ }
588
+
589
+ def output(self):
590
+ return luigi.LocalTarget(str(self.file_path(FilenameEnum.TEAM_MARKDOWN)))
591
+
592
+ def run(self):
593
+ logger.info("TeamMarkdownTask. Loading files...")
594
+
595
+ # 1. Read the team_member_list from EnrichTeamMembersWithEnvironmentInfoTask.
596
+ with self.input()['enrich_team_members_with_environment_info']['clean'].open("r") as f:
597
+ team_member_list = json.load(f)
598
+
599
+ # 2. Read the json from ReviewTeamTask.
600
+ with self.input()['review_team'].open("r") as f:
601
+ review_team_json = json.load(f)
602
+
603
+ logger.info("TeamMarkdownTask. All files are now ready. Processing...")
604
+
605
+ # Combine the team members and the review into a Markdown document.
606
+ builder = TeamMarkdownDocumentBuilder()
607
+ builder.append_roles(team_member_list)
608
+ builder.append_separator()
609
+ builder.append_full_review(review_team_json)
610
+ builder.write_to_file(self.output().path)
611
+
612
+ logger.info("TeamMarkdownTask complete.")
613
+
614
  class SWOTAnalysisTask(PlanTask):
615
  llm_model = luigi.Parameter(default=DEFAULT_LLM_MODEL)
616
 
 
1289
 
1290
  def requires(self):
1291
  return {
1292
+ 'team_markdown': TeamMarkdownTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1293
  'swot_analysis': SWOTAnalysisTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1294
  'pitch_markdown': ConvertPitchToMarkdownTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1295
  'wbs_project123': WBSProjectLevel1AndLevel2AndLevel3Task(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
 
1300
  rg = ReportGenerator()
1301
  rg.append_pitch_markdown(self.input()['pitch_markdown']['markdown'].path)
1302
  rg.append_swot_analysis_markdown(self.input()['swot_analysis']['markdown'].path)
1303
+ rg.append_team_markdown(self.input()['team_markdown'].path)
1304
  rg.append_project_plan_csv(self.input()['wbs_project123']['csv'].path)
1305
  rg.append_expert_criticism_markdown(self.input()['expert_review'].path)
1306
  rg.save_report(self.output().path)
 
1314
  'assumptions': AssumptionsTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1315
  'pre_project_assessment': PreProjectAssessmentTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1316
  'project_plan': ProjectPlanTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1317
+ 'find_team_members': FindTeamMembersTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1318
+ 'enrich_team_members_with_contract_type': EnrichTeamMembersWithContractTypeTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1319
+ 'enrich_team_members_with_background_story': EnrichTeamMembersWithBackgroundStoryTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1320
+ 'enrich_team_members_with_environment_info': EnrichTeamMembersWithEnvironmentInfoTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1321
+ 'review_team': ReviewTeamTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1322
+ 'team_markdown': TeamMarkdownTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1323
  'swot_analysis': SWOTAnalysisTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1324
  'expert_review': ExpertReviewTask(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
1325
  'wbs_level1': CreateWBSLevel1Task(run_id=self.run_id, speedvsdetail=self.speedvsdetail, llm_model=self.llm_model),
src/report/report_generator.py CHANGED
@@ -92,6 +92,12 @@ class ReportGenerator:
92
  if swot_md:
93
  self.report_data['swot'] = swot_md
94
 
 
 
 
 
 
 
95
  def append_expert_criticism_markdown(self, file_path: Path):
96
  """Append the expert criticism markdown to the report."""
97
  expert_md = self.read_markdown_file(file_path)
@@ -210,6 +216,15 @@ class ReportGenerator:
210
  html_parts.append(markdown.markdown(self.report_data['swot']))
211
  html_parts.append("</div>")
212
 
 
 
 
 
 
 
 
 
 
213
  # Expert Criticism
214
  if 'expert_criticism' in self.report_data:
215
  html_parts.append("""
@@ -276,6 +291,7 @@ def main():
276
  report_generator = ReportGenerator()
277
  report_generator.append_pitch_markdown(input_path / FilenameEnum.PITCH_MARKDOWN.value)
278
  report_generator.append_swot_analysis_markdown(input_path / FilenameEnum.SWOT_MARKDOWN.value)
 
279
  report_generator.append_expert_criticism_markdown(input_path / FilenameEnum.EXPERT_CRITICISM_MARKDOWN.value)
280
  report_generator.append_project_plan_csv(input_path / FilenameEnum.WBS_PROJECT_LEVEL1_AND_LEVEL2_AND_LEVEL3_CSV.value)
281
  report_generator.save_report(output_path)
 
92
  if swot_md:
93
  self.report_data['swot'] = swot_md
94
 
95
+ def append_team_markdown(self, file_path: Path):
96
+ """Append the team markdown to the report."""
97
+ swot_md = self.read_markdown_file(file_path)
98
+ if swot_md:
99
+ self.report_data['team'] = swot_md
100
+
101
  def append_expert_criticism_markdown(self, file_path: Path):
102
  """Append the expert criticism markdown to the report."""
103
  expert_md = self.read_markdown_file(file_path)
 
216
  html_parts.append(markdown.markdown(self.report_data['swot']))
217
  html_parts.append("</div>")
218
 
219
+ # Team
220
+ if 'team' in self.report_data:
221
+ html_parts.append("""
222
+ <div class="section">
223
+ <h2>Team</h2>
224
+ """)
225
+ html_parts.append(markdown.markdown(self.report_data['team']))
226
+ html_parts.append("</div>")
227
+
228
  # Expert Criticism
229
  if 'expert_criticism' in self.report_data:
230
  html_parts.append("""
 
291
  report_generator = ReportGenerator()
292
  report_generator.append_pitch_markdown(input_path / FilenameEnum.PITCH_MARKDOWN.value)
293
  report_generator.append_swot_analysis_markdown(input_path / FilenameEnum.SWOT_MARKDOWN.value)
294
+ report_generator.append_team_markdown(input_path / FilenameEnum.TEAM_MARKDOWN.value)
295
  report_generator.append_expert_criticism_markdown(input_path / FilenameEnum.EXPERT_CRITICISM_MARKDOWN.value)
296
  report_generator.append_project_plan_csv(input_path / FilenameEnum.WBS_PROJECT_LEVEL1_AND_LEVEL2_AND_LEVEL3_CSV.value)
297
  report_generator.save_report(output_path)
src/team/create_markdown_document.py DELETED
@@ -1,71 +0,0 @@
1
- import json
2
-
3
- def create_markdown_document(plan_prompt: str, team_member_list_json_file_path: str, output_file_path: str):
4
- """
5
- Reads text content and JSON data, then writes a Markdown document.
6
-
7
- :param text_content: str, the main topic text to include in the Markdown
8
- :param json_file_path: str, path to the JSON file
9
- :param output_file_path: str, path to output the generated Markdown file
10
- """
11
- # Load JSON data
12
- with open(team_member_list_json_file_path, 'r', encoding='utf-8') as f:
13
- roles_data = json.load(f)
14
-
15
- # Begin constructing the Markdown content
16
- rows = []
17
-
18
- # 1. Add the main text content as a top-level section
19
- rows.append("# The plan")
20
- rows.append("")
21
- rows.append(plan_prompt.strip())
22
- rows.append("")
23
- rows.append("---")
24
- rows.append("")
25
-
26
- # 2. Loop through each entry in the JSON and format as Markdown
27
- for entry in roles_data:
28
- # Each entry will be displayed under a subsection
29
- rows.append(f"## Role {entry['id']} - {entry['category']}")
30
- if 'explanation' in entry:
31
- rows.append("")
32
- rows.append(f"**Explanation**:\n{entry['explanation']}")
33
- if 'count' in entry:
34
- rows.append("")
35
- rows.append(f"**People Count**:\n{entry['count']}")
36
- if 'typical_job_activities' in entry:
37
- rows.append("")
38
- rows.append(f"**Typical Activities**:\n{entry['typical_job_activities']}")
39
- if 'background_story' in entry:
40
- rows.append("")
41
- rows.append(f"**Background Story**:\n{entry['background_story']}")
42
- if 'equipment_needs' in entry:
43
- rows.append("")
44
- rows.append(f"**Equipment Needs**:\n{entry['equipment_needs']}")
45
- if 'facility_needs' in entry:
46
- rows.append("")
47
- rows.append(f"**Facility Needs**:\n{entry['facility_needs']}")
48
- rows.append("")
49
- rows.append("---")
50
- rows.append("")
51
-
52
- # Write everything to the output markdown file
53
- with open(output_file_path, 'w', encoding='utf-8') as out_f:
54
- out_f.write("\n".join(rows))
55
-
56
- print(f"Markdown document has been created at: {output_file_path}")
57
-
58
-
59
- if __name__ == "__main__":
60
- # Your text snippet
61
- plan_prompt = "Deep cave exploration to find new lifeforms in extreme conditions."
62
-
63
- # Path to your JSON file
64
- # TODO: Eliminate hardcoded paths
65
- json_path = "/Users/neoneye/Desktop/planexe_data/005-enriched_team_members_list.json"
66
-
67
- # Output Markdown file path
68
- output_path = "output.md"
69
-
70
- # Create the markdown document
71
- create_markdown_document(plan_prompt, json_path, output_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/team/{enrich_team_members.py → enrich_team_members_with_background_story.py} RENAMED
@@ -1,14 +1,20 @@
1
  """
2
  Enrich each team member with a fictional background story and typical job activities.
 
 
3
  """
4
- import os
5
  import json
6
  import time
 
7
  from math import ceil
 
8
  from typing import List, Optional
9
  from pydantic import BaseModel, Field
10
  from llama_index.core.llms import ChatMessage, MessageRole
11
  from llama_index.core.llms.llm import LLM
 
 
 
12
 
13
  class TeamMember(BaseModel):
14
  """A human with domain knowledge."""
@@ -28,6 +34,8 @@ class TeamDetails(BaseModel):
28
  )
29
 
30
  ENRICH_TEAM_MEMBERS_SYSTEM_PROMPT = """
 
 
31
  Write a fictional background story about the person. It must be one paragraph that covers:
32
  - First name and last name.
33
  - Location.
@@ -38,58 +46,113 @@ Write a fictional background story about the person. It must be one paragraph th
38
  The typical_job_activities describes relevant skills needed for this project.
39
  """
40
 
41
- def enrich_team_members(llm: LLM, job_description: str, team_member_list: list, system_prompt: Optional[str]) -> dict:
42
- compact_json = json.dumps(team_member_list, separators=(',', ':'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- user_prompt = f"""Project description:
45
- {job_description}
 
 
 
 
 
 
 
 
 
46
 
47
- Here is the list of team members that needs to be enriched:
48
- {compact_json}
49
- """
50
- # print(user_prompt)
51
 
52
- chat_message_list = []
53
- if system_prompt:
54
- chat_message_list.append(
55
  ChatMessage(
56
  role=MessageRole.SYSTEM,
57
  content=system_prompt,
 
 
 
 
58
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  )
 
60
 
61
- chat_message_user = ChatMessage(
62
- role=MessageRole.USER,
63
- content=user_prompt,
64
- )
65
- chat_message_list.append(chat_message_user)
66
-
67
- sllm = llm.as_structured_llm(TeamDetails)
68
-
69
- start_time = time.perf_counter()
70
- chat_response = sllm.chat(chat_message_list)
71
- end_time = time.perf_counter()
72
- duration = int(ceil(end_time - start_time))
73
-
74
- json_response = json.loads(chat_response.message.content)
75
-
76
- metadata = {
77
- "duration_warmup": duration,
78
- }
79
- json_response['metadata'] = metadata
80
- return json_response
81
-
82
- def cleanup_enriched_team_members_and_merge_with_team_members(raw_enriched_team_member_dict: dict, team_member_list: list) -> list:
83
- result_team_member_list = team_member_list.copy()
84
- enriched_team_member_list = raw_enriched_team_member_dict['team_members']
85
- id_to_enriched_team_member = {item['id']: item for item in enriched_team_member_list}
86
- for team_member in result_team_member_list:
87
- id = team_member['id']
88
- enriched_team_member = id_to_enriched_team_member.get(id)
89
- if enriched_team_member:
90
- team_member['typical_job_activities'] = enriched_team_member['typical_job_activities']
91
- team_member['background_story'] = enriched_team_member['job_background_story_of_employee']
92
- return result_team_member_list
93
 
94
  if __name__ == "__main__":
95
  from src.llm_factory import get_llm
@@ -122,5 +185,13 @@ if __name__ == "__main__":
122
  }
123
  ]
124
 
125
- json_response = enrich_team_members(llm, job_description, team_member_list, ENRICH_TEAM_MEMBERS_SYSTEM_PROMPT)
 
 
 
 
126
  print(json.dumps(json_response, indent=2))
 
 
 
 
 
1
  """
2
  Enrich each team member with a fictional background story and typical job activities.
3
+
4
+ PROMPT> python -m src.team.enrich_team_members_with_background_story
5
  """
 
6
  import json
7
  import time
8
+ import logging
9
  from math import ceil
10
+ from dataclasses import dataclass
11
  from typing import List, Optional
12
  from pydantic import BaseModel, Field
13
  from llama_index.core.llms import ChatMessage, MessageRole
14
  from llama_index.core.llms.llm import LLM
15
+ from src.format_json_for_use_in_query import format_json_for_use_in_query
16
+
17
+ logger = logging.getLogger(__name__)
18
 
19
  class TeamMember(BaseModel):
20
  """A human with domain knowledge."""
 
34
  )
35
 
36
  ENRICH_TEAM_MEMBERS_SYSTEM_PROMPT = """
37
+ For each team member provided, enrich them with a fictional background story and typical job activities.
38
+
39
  Write a fictional background story about the person. It must be one paragraph that covers:
40
  - First name and last name.
41
  - Location.
 
46
  The typical_job_activities describes relevant skills needed for this project.
47
  """
48
 
49
+ @dataclass
50
+ class EnrichTeamMembersWithBackgroundStory:
51
+ """
52
+ Enrich each team member with a fictional background story and typical job activities.
53
+ """
54
+ system_prompt: str
55
+ user_prompt: str
56
+ response: dict
57
+ metadata: dict
58
+ team_member_list: list[dict]
59
+
60
+ @classmethod
61
+ def format_query(cls, job_description: str, team_member_list: list[dict]) -> str:
62
+ if not isinstance(job_description, str):
63
+ raise ValueError("Invalid job_description.")
64
+ if not isinstance(team_member_list, list):
65
+ raise ValueError("Invalid team_member_list.")
66
+
67
+ query = (
68
+ f"Project description:\n{job_description}\n\n"
69
+ f"Here is the list of team members that needs to be enriched:\n{format_json_for_use_in_query(team_member_list)}"
70
+ )
71
+ return query
72
 
73
+ @classmethod
74
+ def execute(cls, llm: LLM, user_prompt: str, team_member_list: list[dict]) -> 'EnrichTeamMembersWithBackgroundStory':
75
+ """
76
+ Invoke LLM with each team member.
77
+ """
78
+ if not isinstance(llm, LLM):
79
+ raise ValueError("Invalid LLM instance.")
80
+ if not isinstance(user_prompt, str):
81
+ raise ValueError("Invalid user_prompt.")
82
+ if not isinstance(team_member_list, list):
83
+ raise ValueError("Invalid team_member_list.")
84
 
85
+ logger.debug(f"User Prompt:\n{user_prompt}")
 
 
 
86
 
87
+ system_prompt = ENRICH_TEAM_MEMBERS_SYSTEM_PROMPT.strip()
88
+
89
+ chat_message_list = [
90
  ChatMessage(
91
  role=MessageRole.SYSTEM,
92
  content=system_prompt,
93
+ ),
94
+ ChatMessage(
95
+ role=MessageRole.USER,
96
+ content=user_prompt,
97
  )
98
+ ]
99
+
100
+ sllm = llm.as_structured_llm(TeamDetails)
101
+ start_time = time.perf_counter()
102
+ try:
103
+ chat_response = sllm.chat(chat_message_list)
104
+ except Exception as e:
105
+ logger.debug(f"LLM chat interaction failed: {e}")
106
+ logger.error("LLM chat interaction failed.", exc_info=True)
107
+ raise ValueError("LLM chat interaction failed.") from e
108
+
109
+ end_time = time.perf_counter()
110
+ duration = int(ceil(end_time - start_time))
111
+ response_byte_count = len(chat_response.message.content.encode('utf-8'))
112
+ logger.info(f"LLM chat interaction completed in {duration} seconds. Response byte count: {response_byte_count}")
113
+
114
+ json_response = chat_response.raw.model_dump()
115
+
116
+ team_member_list_enriched = cls.cleanup_enriched_team_members_and_merge_with_team_members(chat_response.raw, team_member_list)
117
+
118
+ metadata = dict(llm.metadata)
119
+ metadata["llm_classname"] = llm.class_name()
120
+ metadata["duration"] = duration
121
+ metadata["response_byte_count"] = response_byte_count
122
+
123
+ result = EnrichTeamMembersWithBackgroundStory(
124
+ system_prompt=system_prompt,
125
+ user_prompt=user_prompt,
126
+ response=json_response,
127
+ metadata=metadata,
128
+ team_member_list=team_member_list_enriched,
129
  )
130
+ return result
131
 
132
+ def to_dict(self, include_metadata=True, include_system_prompt=True, include_user_prompt=True) -> dict:
133
+ d = self.response.copy()
134
+ if include_metadata:
135
+ d['metadata'] = self.metadata
136
+ if include_system_prompt:
137
+ d['system_prompt'] = self.system_prompt
138
+ if include_user_prompt:
139
+ d['user_prompt'] = self.user_prompt
140
+ return d
141
+
142
+ def cleanup_enriched_team_members_and_merge_with_team_members(team_details: TeamDetails, team_member_list: list[dict]) -> list[dict]:
143
+ result_team_member_list = team_member_list.copy()
144
+ enriched_team_member_list = team_details.team_members
145
+ id_to_enriched_team_member = {item.id: item for item in enriched_team_member_list}
146
+ for team_member_index, team_member in enumerate(result_team_member_list):
147
+ if not 'id' in team_member:
148
+ logger.warning(f"Team member #{team_member_index} does not have an id")
149
+ continue
150
+ id = team_member['id']
151
+ enriched_team_member = id_to_enriched_team_member.get(id)
152
+ if enriched_team_member:
153
+ team_member['typical_job_activities'] = enriched_team_member.typical_job_activities
154
+ team_member['background_story'] = enriched_team_member.job_background_story_of_employee
155
+ return result_team_member_list
 
 
 
 
 
 
 
 
156
 
157
  if __name__ == "__main__":
158
  from src.llm_factory import get_llm
 
185
  }
186
  ]
187
 
188
+ query = EnrichTeamMembersWithBackgroundStory.format_query(job_description, team_member_list)
189
+ print(f"Query:\n{query}\n\n")
190
+
191
+ enrich_team_members_with_background_story = EnrichTeamMembersWithBackgroundStory.execute(llm, query, team_member_list)
192
+ json_response = enrich_team_members_with_background_story.to_dict(include_system_prompt=False, include_user_prompt=False)
193
  print(json.dumps(json_response, indent=2))
194
+
195
+ print("\n\nTeam members with extra details:")
196
+ enriched_json = enrich_team_members_with_background_story.team_member_list
197
+ print(json.dumps(enriched_json, indent=2))
src/team/enrich_team_members_with_contract_type.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Enrich the team members with what kind of contract type they have.
3
+
4
+ PROMPT> python -m src.team.enrich_team_members_with_contract_type
5
+ """
6
+ import os
7
+ import json
8
+ import time
9
+ import logging
10
+ from enum import Enum
11
+ from math import ceil
12
+ from dataclasses import dataclass
13
+ from typing import List, Optional
14
+ from pydantic import BaseModel, Field
15
+ from llama_index.core.llms import ChatMessage, MessageRole
16
+ from llama_index.core.llms.llm import LLM
17
+ from src.format_json_for_use_in_query import format_json_for_use_in_query
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ class ContractType(str, Enum):
22
+ # Full-Time Employee. The individual is a permanent member of the organization.
23
+ full_time_employee = 'full_time_employee'
24
+ # Part-Time Employee. Similar to a full-time employee, but they work fewer hours per week.
25
+ part_time_employee = 'part_time_employee'
26
+ # Independent Contractor / Consultant. A self-employed individual or business that provides services to your company on a contractual basis.
27
+ independent_contractor = 'independent_contractor'
28
+ # Temporary Employee through an Agency. The individual is employed by a staffing agency, who then assigns them to work at your company for a set period.
29
+ agency_temp = 'agency_temp'
30
+ # Other. When the contract type does not fit into the above categories.
31
+ other = 'other'
32
+
33
+ class TeamMember(BaseModel):
34
+ """A human with domain knowledge."""
35
+ id: int = Field(
36
+ description="A unique id for the job_category."
37
+ )
38
+ contract_type: ContractType = Field(
39
+ description="The legal and financial agreement."
40
+ )
41
+ justification: str = Field(
42
+ description="Brief explanation for why that contract type was chosen. Helps justify decisions and allows for easy review."
43
+ )
44
+
45
+ class DocumentDetails(BaseModel):
46
+ team_members: list[TeamMember] = Field(
47
+ description="The experts with domain knowledge about the problem."
48
+ )
49
+
50
+ ENRICH_TEAM_MEMBERS_CONTRACT_TYPE_SYSTEM_PROMPT = """
51
+ You are an expert at determining what contract type are needed for different job roles given a project description.
52
+
53
+ "Contract Type" refers to the legal and financial agreement you have with each individual working on the project.
54
+ It dictates their employment status, compensation, benefits, and overall relationship with your organization (or the project).
55
+
56
+ The "contract_type" for each team member is crucial for the following reasons:
57
+ - Drives Cost Calculations: The type of employment agreement dictates a huge portion of project labor costs. Whether you're paying salary + benefits, or a fixed project fee, this is foundational information.
58
+ - Impacts Availability and Control: The contract type determines how much control you have over the person and how readily available they will be.
59
+ - Informs Resource Planning: It influences long-term versus short-term resource commitments.
60
+
61
+ Allowed values: "full_time_employee", "part_time_employee", "independent_contractor", "agency_temp"
62
+
63
+ For each team member provided, identify the contract_type considering the given project description. Provide concise but descriptive answers.
64
+ """
65
+
66
+ @dataclass
67
+ class EnrichTeamMembersWithContractType:
68
+ """
69
+ Enrich each team member with more info.
70
+ """
71
+ system_prompt: str
72
+ user_prompt: str
73
+ response: dict
74
+ metadata: dict
75
+ team_member_list: list[dict]
76
+
77
+ @classmethod
78
+ def format_query(cls, job_description: str, team_member_list: list[dict]) -> str:
79
+ if not isinstance(job_description, str):
80
+ raise ValueError("Invalid job_description.")
81
+ if not isinstance(team_member_list, list):
82
+ raise ValueError("Invalid team_member_list.")
83
+
84
+ query = (
85
+ f"Project description:\n{job_description}\n\n"
86
+ f"Here is the list of team members that needs to be enriched:\n{format_json_for_use_in_query(team_member_list)}"
87
+ )
88
+ return query
89
+
90
+ @classmethod
91
+ def execute(cls, llm: LLM, user_prompt: str, team_member_list: list[dict]) -> 'EnrichTeamMembersWithContractType':
92
+ """
93
+ Invoke LLM with each team member.
94
+ """
95
+ if not isinstance(llm, LLM):
96
+ raise ValueError("Invalid LLM instance.")
97
+ if not isinstance(user_prompt, str):
98
+ raise ValueError("Invalid user_prompt.")
99
+ if not isinstance(team_member_list, list):
100
+ raise ValueError("Invalid team_member_list.")
101
+
102
+ logger.debug(f"User Prompt:\n{user_prompt}")
103
+
104
+ system_prompt = ENRICH_TEAM_MEMBERS_CONTRACT_TYPE_SYSTEM_PROMPT.strip()
105
+
106
+ chat_message_list = [
107
+ ChatMessage(
108
+ role=MessageRole.SYSTEM,
109
+ content=system_prompt,
110
+ ),
111
+ ChatMessage(
112
+ role=MessageRole.USER,
113
+ content=user_prompt,
114
+ )
115
+ ]
116
+
117
+ sllm = llm.as_structured_llm(DocumentDetails)
118
+ start_time = time.perf_counter()
119
+ try:
120
+ chat_response = sllm.chat(chat_message_list)
121
+ except Exception as e:
122
+ logger.debug(f"LLM chat interaction failed: {e}")
123
+ logger.error("LLM chat interaction failed.", exc_info=True)
124
+ raise ValueError("LLM chat interaction failed.") from e
125
+
126
+ end_time = time.perf_counter()
127
+ duration = int(ceil(end_time - start_time))
128
+ response_byte_count = len(chat_response.message.content.encode('utf-8'))
129
+ logger.info(f"LLM chat interaction completed in {duration} seconds. Response byte count: {response_byte_count}")
130
+
131
+ json_response = chat_response.raw.model_dump()
132
+
133
+ team_member_list_enriched = cls.cleanup_and_merge_with_team_members(chat_response.raw, team_member_list)
134
+
135
+ metadata = dict(llm.metadata)
136
+ metadata["llm_classname"] = llm.class_name()
137
+ metadata["duration"] = duration
138
+ metadata["response_byte_count"] = response_byte_count
139
+
140
+ result = EnrichTeamMembersWithContractType(
141
+ system_prompt=system_prompt,
142
+ user_prompt=user_prompt,
143
+ response=json_response,
144
+ metadata=metadata,
145
+ team_member_list=team_member_list_enriched,
146
+ )
147
+ return result
148
+
149
+ def to_dict(self, include_metadata=True, include_system_prompt=True, include_user_prompt=True) -> dict:
150
+ d = self.response.copy()
151
+ if include_metadata:
152
+ d['metadata'] = self.metadata
153
+ if include_system_prompt:
154
+ d['system_prompt'] = self.system_prompt
155
+ if include_user_prompt:
156
+ d['user_prompt'] = self.user_prompt
157
+ return d
158
+
159
+ def cleanup_and_merge_with_team_members(document_details: DocumentDetails, team_member_list: list[dict]) -> list:
160
+ result_team_member_list = team_member_list.copy()
161
+ enriched_team_member_list = document_details.team_members
162
+ id_to_enriched_team_member = {item.id: item for item in enriched_team_member_list}
163
+ for team_member_index, team_member in enumerate(result_team_member_list):
164
+ if not 'id' in team_member:
165
+ logger.warning(f"Team member #{team_member_index} does not have an id")
166
+ continue
167
+ id = team_member['id']
168
+ enriched_team_member = id_to_enriched_team_member.get(id)
169
+ if enriched_team_member:
170
+ team_member['contract_type'] = enriched_team_member.contract_type.value
171
+ team_member['contract_type_justification'] = enriched_team_member.justification
172
+ return result_team_member_list
173
+
174
+ if __name__ == "__main__":
175
+ from src.llm_factory import get_llm
176
+
177
+ llm = get_llm("ollama-llama3.1")
178
+ # llm = get_llm("deepseek-chat")
179
+
180
+ job_description = "Establish a new police station in a high crime area."
181
+
182
+ team_member_list = [
183
+ {
184
+ "id": 1,
185
+ "category": "Law Enforcement",
186
+ "explanation": "Police officers and detectives are essential for patrolling, investigation, and maintaining public safety."
187
+ },
188
+ {
189
+ "id": 2,
190
+ "category": "Administration",
191
+ "explanation": "Administrative staff manage paperwork, scheduling, and coordination of police activities."
192
+ },
193
+ {
194
+ "id": 3,
195
+ "category": "Forensics",
196
+ "explanation": "Forensic experts analyze crime scene evidence to support investigations."
197
+ },
198
+ {
199
+ "id": 4,
200
+ "category": "Community Relations",
201
+ "explanation": "Officers or liaisons engage with the community to build trust and cooperation."
202
+ }
203
+ ]
204
+
205
+ query = EnrichTeamMembersWithContractType.format_query(job_description, team_member_list)
206
+ print(f"Query:\n{query}\n\n")
207
+
208
+ enrich_team_members_with_environment_info = EnrichTeamMembersWithContractType.execute(llm, query, team_member_list)
209
+ json_response = enrich_team_members_with_environment_info.to_dict(include_system_prompt=False, include_user_prompt=False)
210
+ print(json.dumps(json_response, indent=2))
211
+
212
+ print("\n\nTeam members:")
213
+ json_team = enrich_team_members_with_environment_info.team_member_list
214
+ print(json.dumps(json_team, indent=2))
src/team/enrich_team_members_with_environment_info.py CHANGED
@@ -1,14 +1,21 @@
1
  """
2
  Enrich the team members with what kind of equipment and facilities they need for the task.
 
 
3
  """
4
  import os
5
  import json
6
  import time
 
7
  from math import ceil
 
8
  from typing import List, Optional
9
  from pydantic import BaseModel, Field
10
  from llama_index.core.llms import ChatMessage, MessageRole
11
  from llama_index.core.llms.llm import LLM
 
 
 
12
 
13
  class TeamMember(BaseModel):
14
  """A human with domain knowledge."""
@@ -28,60 +35,118 @@ class DocumentDetails(BaseModel):
28
  )
29
 
30
  ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_SYSTEM_PROMPT = """
31
- Identify what kind of equipment and facilities are needed for the daily job of each team member.
 
 
32
  """
33
 
34
- def enrich_team_members_with_environment_info(llm: LLM, job_description: str, team_member_list: list, system_prompt: Optional[str]) -> dict:
35
- compact_json = json.dumps(team_member_list, separators=(',', ':'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- user_prompt = f"""Project description:
38
- {job_description}
 
 
 
 
 
 
 
 
 
39
 
40
- Here is the list of team members that needs to be enriched:
41
- {compact_json}
42
- """
43
- chat_message_list = []
44
- if system_prompt:
45
- chat_message_list.append(
46
  ChatMessage(
47
  role=MessageRole.SYSTEM,
48
  content=system_prompt,
 
 
 
 
49
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  )
 
51
 
52
- chat_message_user = ChatMessage(
53
- role=MessageRole.USER,
54
- content=user_prompt,
55
- )
56
- chat_message_list.append(chat_message_user)
57
-
58
- sllm = llm.as_structured_llm(DocumentDetails)
59
-
60
- start_time = time.perf_counter()
61
- chat_response = sllm.chat(chat_message_list)
62
- end_time = time.perf_counter()
63
-
64
- json_response = json.loads(chat_response.message.content)
65
-
66
- duration = int(ceil(end_time - start_time))
67
-
68
- metadata = {
69
- "duration": duration,
70
- }
71
- json_response['metadata'] = metadata
72
- return json_response
73
-
74
- def cleanup_enriched_team_members_with_environment_info_and_merge_with_team_members(raw_enriched_team_member_dict: dict, team_member_list: list) -> list:
75
- result_team_member_list = team_member_list.copy()
76
- enriched_team_member_list = raw_enriched_team_member_dict['team_members']
77
- id_to_enriched_team_member = {item['id']: item for item in enriched_team_member_list}
78
- for team_member in result_team_member_list:
79
- id = team_member['id']
80
- enriched_team_member = id_to_enriched_team_member.get(id)
81
- if enriched_team_member:
82
- team_member['equipment_needs'] = enriched_team_member['equipment_needs']
83
- team_member['facility_needs'] = enriched_team_member['facility_needs']
84
- return result_team_member_list
85
 
86
  if __name__ == "__main__":
87
  from src.llm_factory import get_llm
@@ -114,5 +179,13 @@ if __name__ == "__main__":
114
  }
115
  ]
116
 
117
- json_response = enrich_team_members_with_environment_info(llm, job_description, team_member_list, ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_SYSTEM_PROMPT)
 
 
 
 
118
  print(json.dumps(json_response, indent=2))
 
 
 
 
 
1
  """
2
  Enrich the team members with what kind of equipment and facilities they need for the task.
3
+
4
+ PROMPT> python -m src.team.enrich_team_members_with_environment_info
5
  """
6
  import os
7
  import json
8
  import time
9
+ import logging
10
  from math import ceil
11
+ from dataclasses import dataclass
12
  from typing import List, Optional
13
  from pydantic import BaseModel, Field
14
  from llama_index.core.llms import ChatMessage, MessageRole
15
  from llama_index.core.llms.llm import LLM
16
+ from src.format_json_for_use_in_query import format_json_for_use_in_query
17
+
18
+ logger = logging.getLogger(__name__)
19
 
20
  class TeamMember(BaseModel):
21
  """A human with domain knowledge."""
 
35
  )
36
 
37
  ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_SYSTEM_PROMPT = """
38
+ You are an expert at determining what equipment and facilities are needed for different job roles given a project description.
39
+
40
+ For each team member provided, identify the specific equipment and facilities they require to effectively perform their daily tasks within the context of the given project description. Provide concise but descriptive answers.
41
  """
42
 
43
+ @dataclass
44
+ class EnrichTeamMembersWithEnvironmentInfo:
45
+ """
46
+ Enrich each team member with more info.
47
+ """
48
+ system_prompt: str
49
+ user_prompt: str
50
+ response: dict
51
+ metadata: dict
52
+ team_member_list: list[dict]
53
+
54
+ @classmethod
55
+ def format_query(cls, job_description: str, team_member_list: list[dict]) -> str:
56
+ if not isinstance(job_description, str):
57
+ raise ValueError("Invalid job_description.")
58
+ if not isinstance(team_member_list, list):
59
+ raise ValueError("Invalid team_member_list.")
60
+
61
+ query = (
62
+ f"Project description:\n{job_description}\n\n"
63
+ f"Here is the list of team members that needs to be enriched:\n{format_json_for_use_in_query(team_member_list)}"
64
+ )
65
+ return query
66
 
67
+ @classmethod
68
+ def execute(cls, llm: LLM, user_prompt: str, team_member_list: list[dict]) -> 'EnrichTeamMembersWithEnvironmentInfo':
69
+ """
70
+ Invoke LLM with each team member.
71
+ """
72
+ if not isinstance(llm, LLM):
73
+ raise ValueError("Invalid LLM instance.")
74
+ if not isinstance(user_prompt, str):
75
+ raise ValueError("Invalid user_prompt.")
76
+ if not isinstance(team_member_list, list):
77
+ raise ValueError("Invalid team_member_list.")
78
 
79
+ logger.debug(f"User Prompt:\n{user_prompt}")
80
+
81
+ system_prompt = ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_SYSTEM_PROMPT.strip()
82
+
83
+ chat_message_list = [
 
84
  ChatMessage(
85
  role=MessageRole.SYSTEM,
86
  content=system_prompt,
87
+ ),
88
+ ChatMessage(
89
+ role=MessageRole.USER,
90
+ content=user_prompt,
91
  )
92
+ ]
93
+
94
+ sllm = llm.as_structured_llm(DocumentDetails)
95
+ start_time = time.perf_counter()
96
+ try:
97
+ chat_response = sllm.chat(chat_message_list)
98
+ except Exception as e:
99
+ logger.debug(f"LLM chat interaction failed: {e}")
100
+ logger.error("LLM chat interaction failed.", exc_info=True)
101
+ raise ValueError("LLM chat interaction failed.") from e
102
+
103
+ end_time = time.perf_counter()
104
+ duration = int(ceil(end_time - start_time))
105
+ response_byte_count = len(chat_response.message.content.encode('utf-8'))
106
+ logger.info(f"LLM chat interaction completed in {duration} seconds. Response byte count: {response_byte_count}")
107
+
108
+ json_response = chat_response.raw.model_dump()
109
+
110
+ team_member_list_enriched = cls.cleanup_enriched_team_members_with_environment_info_and_merge_with_team_members(chat_response.raw, team_member_list)
111
+
112
+ metadata = dict(llm.metadata)
113
+ metadata["llm_classname"] = llm.class_name()
114
+ metadata["duration"] = duration
115
+ metadata["response_byte_count"] = response_byte_count
116
+
117
+ result = EnrichTeamMembersWithEnvironmentInfo(
118
+ system_prompt=system_prompt,
119
+ user_prompt=user_prompt,
120
+ response=json_response,
121
+ metadata=metadata,
122
+ team_member_list=team_member_list_enriched,
123
  )
124
+ return result
125
 
126
+ def to_dict(self, include_metadata=True, include_system_prompt=True, include_user_prompt=True) -> dict:
127
+ d = self.response.copy()
128
+ if include_metadata:
129
+ d['metadata'] = self.metadata
130
+ if include_system_prompt:
131
+ d['system_prompt'] = self.system_prompt
132
+ if include_user_prompt:
133
+ d['user_prompt'] = self.user_prompt
134
+ return d
135
+
136
+ def cleanup_enriched_team_members_with_environment_info_and_merge_with_team_members(document_details: DocumentDetails, team_member_list: list[dict]) -> list:
137
+ result_team_member_list = team_member_list.copy()
138
+ enriched_team_member_list = document_details.team_members
139
+ id_to_enriched_team_member = {item.id: item for item in enriched_team_member_list}
140
+ for team_member_index, team_member in enumerate(result_team_member_list):
141
+ if not 'id' in team_member:
142
+ logger.warning(f"Team member #{team_member_index} does not have an id")
143
+ continue
144
+ id = team_member['id']
145
+ enriched_team_member = id_to_enriched_team_member.get(id)
146
+ if enriched_team_member:
147
+ team_member['equipment_needs'] = enriched_team_member.equipment_needs
148
+ team_member['facility_needs'] = enriched_team_member.facility_needs
149
+ return result_team_member_list
 
 
 
 
 
 
 
 
 
150
 
151
  if __name__ == "__main__":
152
  from src.llm_factory import get_llm
 
179
  }
180
  ]
181
 
182
+ query = EnrichTeamMembersWithEnvironmentInfo.format_query(job_description, team_member_list)
183
+ print(f"Query:\n{query}\n\n")
184
+
185
+ enrich_team_members_with_environment_info = EnrichTeamMembersWithEnvironmentInfo.execute(llm, query, team_member_list)
186
+ json_response = enrich_team_members_with_environment_info.to_dict(include_system_prompt=False, include_user_prompt=False)
187
  print(json.dumps(json_response, indent=2))
188
+
189
+ print("\n\nTeam members:")
190
+ json_team = enrich_team_members_with_environment_info.team_member_list
191
+ print(json.dumps(json_team, indent=2))
src/team/find_team_members.py CHANGED
@@ -6,12 +6,16 @@ PROMPT> python -m src.team.find_team_members
6
  import os
7
  import json
8
  import time
 
9
  from math import ceil
 
10
  from typing import List, Optional
11
  from pydantic import BaseModel, Field
12
  from llama_index.core.llms import ChatMessage, MessageRole
13
  from llama_index.core.llms.llm import LLM
14
 
 
 
15
  class TeamMember(BaseModel):
16
  job_category_title: str = Field(
17
  description="Human readable title"
@@ -22,6 +26,9 @@ class TeamMember(BaseModel):
22
  people_needed: str = Field(
23
  description="Number of people needed."
24
  )
 
 
 
25
 
26
  class DocumentDetails(BaseModel):
27
  brainstorm_of_needed_team_members: list[TeamMember] = Field(
@@ -29,68 +36,147 @@ class DocumentDetails(BaseModel):
29
  )
30
 
31
  FIND_TEAM_MEMBERS_SYSTEM_PROMPT = """
32
- Pick a good team of humans for solving the task.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- Ensure that the team consists of at least 5 members.
 
35
 
36
- The "job_category_title" field must be a brief the title that captures what the role is about.
37
- Such as area of expertise, group, subject, topic or domain.
 
 
 
38
 
39
- Estimate how many people are needed for each role.
40
- What is the minimum of people needed for the role.
41
- What is the maximum of people needed for the role.
42
- Use the people_needed field to specify the number of people needed for each role:
43
- - if only one person is needed, use "1".
44
- - if two people are needed, use "2".
45
- - When it can vary, use "min 5,max 10,depending on capacity".
46
 
47
- Provide a short explanation of why that team member is relevant for the project.
 
 
 
 
 
 
 
 
 
 
 
48
  """
49
 
50
- def find_team_members(llm: LLM, query: str, system_prompt: Optional[str]) -> dict:
51
- chat_message_list = []
52
- if system_prompt:
53
- chat_message_list.append(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  ChatMessage(
55
  role=MessageRole.SYSTEM,
56
  content=system_prompt,
 
 
 
 
57
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- chat_message_user = ChatMessage(
61
- role=MessageRole.USER,
62
- content=query,
63
- )
64
- chat_message_list.append(chat_message_user)
65
-
66
- sllm = llm.as_structured_llm(DocumentDetails)
67
-
68
- start_time = time.perf_counter()
69
- chat_response = sllm.chat(chat_message_list)
70
- end_time = time.perf_counter()
71
- duration = int(ceil(end_time - start_time))
72
-
73
- json_response = json.loads(chat_response.message.content)
74
-
75
- metadata = {
76
- "duration": duration,
77
- }
78
- json_response['metadata'] = metadata
79
- return json_response
80
-
81
- def cleanup_team_members_and_assign_id(team_member_dict: dict) -> list:
82
- result_list = []
83
- team_members = team_member_dict['brainstorm_of_needed_team_members']
84
- for i, team_member in enumerate(team_members, start=1):
85
- item = {
86
- "id": i,
87
- "category": team_member['job_category_title'],
88
- "explanation": team_member['short_explanation'],
89
- "count": team_member['people_needed'],
90
- }
91
- result_list.append(item)
92
- return result_list
93
-
94
  if __name__ == "__main__":
95
  from src.llm_factory import get_llm
96
  from src.plan.find_plan_prompt import find_plan_prompt
@@ -99,5 +185,10 @@ if __name__ == "__main__":
99
  plan_prompt = find_plan_prompt("4dc34d55-0d0d-4e9d-92f4-23765f49dd29")
100
 
101
  print(f"Query:\n{plan_prompt}\n\n")
102
- json_response = find_team_members(llm, plan_prompt, FIND_TEAM_MEMBERS_SYSTEM_PROMPT)
 
103
  print(json.dumps(json_response, indent=2))
 
 
 
 
 
6
  import os
7
  import json
8
  import time
9
+ import logging
10
  from math import ceil
11
+ from dataclasses import dataclass
12
  from typing import List, Optional
13
  from pydantic import BaseModel, Field
14
  from llama_index.core.llms import ChatMessage, MessageRole
15
  from llama_index.core.llms.llm import LLM
16
 
17
+ logger = logging.getLogger(__name__)
18
+
19
  class TeamMember(BaseModel):
20
  job_category_title: str = Field(
21
  description="Human readable title"
 
26
  people_needed: str = Field(
27
  description="Number of people needed."
28
  )
29
+ consequences_of_not_having_this_role: str = Field(
30
+ description="Consequences of not having this role."
31
+ )
32
 
33
  class DocumentDetails(BaseModel):
34
  brainstorm_of_needed_team_members: list[TeamMember] = Field(
 
36
  )
37
 
38
  FIND_TEAM_MEMBERS_SYSTEM_PROMPT = """
39
+ You are a versatile project planning assistant and team architect. Your goal is to analyze the user's project description and decompose it into a comprehensive plan with a focus on human roles and resource allocation—**do not generate any code or technical implementation details.**
40
+
41
+ If the project description involves programming tasks or includes requests for code, treat it as a planning challenge. Instead of writing a script or providing code, break down the project into essential phases and identify the key human roles needed to successfully complete the project.
42
+
43
+ Based on the user's project description, brainstorm a team of potential human support roles that cover all crucial aspects of the project, including planning & preparation, execution, monitoring & adjustment, and maintenance & sustainability.
44
+
45
+ **Output Requirements:**
46
+
47
+ 1. **Team Size:**
48
+ Your output **must include exactly 8 candidate roles**.
49
+ - If your initial analysis identifies fewer than 8 distinct roles, create additional meaningful roles to reach exactly 8.
50
+ - If your analysis results in more than 8 roles, consolidate or combine roles so that the final output contains exactly 8 candidates.
51
+
52
+ 2. **Role Titles:**
53
+ Provide a clear and concise `job_category_title` that accurately describes the role's primary contribution.
54
+
55
+ 3. **Role Explanations:**
56
+ Briefly explain each role’s purpose, key responsibilities, and how it contributes actively throughout the project.
57
 
58
+ 4. **Consequences:**
59
+ For each role, note potential risks or consequences of omitting that role.
60
 
61
+ 5. **People Count / Resource Level:**
62
+ Use the `people_needed` field to indicate the number of people required for each role. **Do not simply default to "1" for every role.** Instead, evaluate the complexity and workload of the role relative to the project's scale:
63
+ - **Single Resource:** If one person is clearly sufficient, use "1".
64
+ - **Fixed Level:** If the role consistently requires a specific number of people (e.g., "2" or "3"), use that fixed number.
65
+ - **Variable Level:** If the required support may vary based on factors like project scale, workload, or budget, specify a range. For example, instead of "1", you might write "min 1, max 3, depending on project scale and workload." Be sure to justify why the role may require more than one person.
66
 
67
+ 6. **Project Phases / Support Stages:**
68
+ Ensure the roles collectively address the following phases:
69
+ - **Planning & Preparation**
70
+ - **Execution**
71
+ - **Monitoring & Adjustment**
72
+ - **Maintenance & Sustainability**
 
73
 
74
+ **Essential Considerations for EVERY Role:**
75
+
76
+ - **Specific Expertise**
77
+ - **Key Responsibilities**
78
+ - **Direct Impact (if applicable)**
79
+ - **Project Dependencies**
80
+ - **Relevant Skills**
81
+ - **Role Priority**
82
+
83
+ **Important:**
84
+ - Do not provide any code or implementation details—even if the prompt is programming-related. Focus solely on planning, decomposing the work, and identifying the essential human roles.
85
+ - **For personal, trivial, or non-commercial projects, avoid suggesting overly formal or business-oriented roles (e.g., Marketing Specialist, Legal Advisor, Technical Support Specialist) unless they are absolutely necessary.** In such cases, prefer roles that can be integrated or scaled down to suit the project's nature.
86
  """
87
 
88
+ @dataclass
89
+ class FindTeamMembers:
90
+ """
91
+ From a project description, find a team for solving the job.
92
+ """
93
+ system_prompt: str
94
+ user_prompt: str
95
+ response: dict
96
+ metadata: dict
97
+ team_member_list: list[dict]
98
+
99
+ @classmethod
100
+ def execute(cls, llm: LLM, user_prompt: str) -> 'FindTeamMembers':
101
+ """
102
+ Invoke LLM to find a team.
103
+ """
104
+ if not isinstance(llm, LLM):
105
+ raise ValueError("Invalid LLM instance.")
106
+ if not isinstance(user_prompt, str):
107
+ raise ValueError("Invalid user_prompt.")
108
+
109
+ logger.debug(f"User Prompt:\n{user_prompt}")
110
+
111
+ system_prompt = FIND_TEAM_MEMBERS_SYSTEM_PROMPT.strip()
112
+
113
+ chat_message_list = [
114
  ChatMessage(
115
  role=MessageRole.SYSTEM,
116
  content=system_prompt,
117
+ ),
118
+ ChatMessage(
119
+ role=MessageRole.USER,
120
+ content=user_prompt,
121
  )
122
+ ]
123
+
124
+ sllm = llm.as_structured_llm(DocumentDetails)
125
+ start_time = time.perf_counter()
126
+ try:
127
+ chat_response = sllm.chat(chat_message_list)
128
+ except Exception as e:
129
+ logger.debug(f"LLM chat interaction failed: {e}")
130
+ logger.error("LLM chat interaction failed.", exc_info=True)
131
+ raise ValueError("LLM chat interaction failed.") from e
132
+
133
+ end_time = time.perf_counter()
134
+ duration = int(ceil(end_time - start_time))
135
+ response_byte_count = len(chat_response.message.content.encode('utf-8'))
136
+ logger.info(f"LLM chat interaction completed in {duration} seconds. Response byte count: {response_byte_count}")
137
+
138
+ json_response = chat_response.raw.model_dump()
139
+
140
+ team_member_list = cls.cleanup_team_members_and_assign_id(chat_response.raw)
141
+
142
+ metadata = dict(llm.metadata)
143
+ metadata["llm_classname"] = llm.class_name()
144
+ metadata["duration"] = duration
145
+ metadata["response_byte_count"] = response_byte_count
146
+
147
+ result = FindTeamMembers(
148
+ system_prompt=system_prompt,
149
+ user_prompt=user_prompt,
150
+ response=json_response,
151
+ metadata=metadata,
152
+ team_member_list=team_member_list,
153
  )
154
+ return result
155
+
156
+ def to_dict(self, include_metadata=True, include_system_prompt=True, include_user_prompt=True) -> dict:
157
+ d = self.response.copy()
158
+ if include_metadata:
159
+ d['metadata'] = self.metadata
160
+ if include_system_prompt:
161
+ d['system_prompt'] = self.system_prompt
162
+ if include_user_prompt:
163
+ d['user_prompt'] = self.user_prompt
164
+ return d
165
+
166
+ def cleanup_team_members_and_assign_id(document_details: DocumentDetails) -> list:
167
+ result_list = []
168
+ team_members = document_details.brainstorm_of_needed_team_members
169
+ for i, team_member in enumerate(team_members, start=1):
170
+ item = {
171
+ "id": i,
172
+ "category": team_member.job_category_title,
173
+ "explanation": team_member.short_explanation,
174
+ "consequences": team_member.consequences_of_not_having_this_role,
175
+ "count": team_member.people_needed,
176
+ }
177
+ result_list.append(item)
178
+ return result_list
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  if __name__ == "__main__":
181
  from src.llm_factory import get_llm
182
  from src.plan.find_plan_prompt import find_plan_prompt
 
185
  plan_prompt = find_plan_prompt("4dc34d55-0d0d-4e9d-92f4-23765f49dd29")
186
 
187
  print(f"Query:\n{plan_prompt}\n\n")
188
+ result = FindTeamMembers.execute(llm, plan_prompt)
189
+ json_response = result.to_dict(include_system_prompt=False, include_user_prompt=False)
190
  print(json.dumps(json_response, indent=2))
191
+
192
+ print("\n\nTeam members:")
193
+ json_team = result.team_member_list
194
+ print(json.dumps(json_team, indent=2))
src/team/review_team.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Review the team.
3
+
4
+ PROMPT> python -m src.team.review_team
5
+ """
6
+ import os
7
+ import json
8
+ import time
9
+ import logging
10
+ from math import ceil
11
+ from dataclasses import dataclass
12
+ from pydantic import BaseModel, Field
13
+ from llama_index.core.llms import ChatMessage, MessageRole
14
+ from llama_index.core.llms.llm import LLM
15
+ from src.format_json_for_use_in_query import format_json_for_use_in_query
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class ReviewItem(BaseModel):
20
+ issue: str = Field(
21
+ description="A brief title or name for the omission/improvement."
22
+ )
23
+ explanation: str = Field(
24
+ description="A concise description of why this issue is important."
25
+ )
26
+ recommendation: str = Field(
27
+ description="Specific suggestions on how to address the issue."
28
+ )
29
+
30
+ class DocumentDetails(BaseModel):
31
+ omissions: list[ReviewItem] = Field(
32
+ description="The most significant omissions."
33
+ )
34
+ potential_improvements: list[ReviewItem] = Field(
35
+ description="Suggestions and recommendations."
36
+ )
37
+
38
+ REVIEW_TEAM_SYSTEM_PROMPT = """
39
+ You are an expert in designing and evaluating team structures for projects of all scales—from personal or trivial endeavors to large, complex initiatives. Your task is to review a team document that includes a project plan, detailed team roles, and sections on omissions and potential improvements.
40
+
41
+ In your analysis, please:
42
+
43
+ 1. **Review the Team Composition:**
44
+ - Examine the team roles described, including details such as contract types, typical activities, background stories, and resource needs.
45
+ - Consider whether the roles sufficiently cover all aspects of the project given its scope.
46
+
47
+ 2. **Identify Omissions:**
48
+ - Highlight any significant missing roles, support functions, or expertise areas that are critical for the project's success.
49
+ - **Important:** When the project is personal or trivial, avoid suggesting overly formal or business-oriented roles (e.g., Marketing Specialist, Legal Advisor, Technical Support Specialist). Instead, suggest simpler or integrative adjustments suitable for a personal context.
50
+
51
+ 3. **Suggest Potential Improvements:**
52
+ - Recommend actionable changes that enhance the team's overall effectiveness, communication, and clarity.
53
+ - Focus on clarifying responsibilities and reducing overlap.
54
+ - For personal or non-commercial projects, tailor your recommendations to be straightforward and avoid introducing new formal roles that are unnecessary.
55
+
56
+ 4. **Provide Actionable Recommendations:**
57
+ - For each identified omission or improvement, offer specific, practical advice on how to address the issue.
58
+ - Ensure your recommendations are scaled appropriately to the project's nature.
59
+
60
+ Your output must be a JSON object with two top-level keys: "omissions" and "potential_improvements". Each key should map to an array of objects, where each object contains:
61
+ - `"issue"`: A brief title summarizing the omission or improvement.
62
+ - `"explanation"`: A concise explanation of why this issue is significant in relation to the project's goals.
63
+ - `"recommendation"`: Specific, actionable advice on how to address the issue.
64
+
65
+ Ensure your JSON output strictly follows this structure without any additional commentary or text.
66
+ """
67
+
68
+ @dataclass
69
+ class ReviewTeam:
70
+ """
71
+ Take a look at the proposed team and provide feedback on potential omissions and improvements.
72
+ """
73
+ system_prompt: str
74
+ user_prompt: str
75
+ response: dict
76
+ metadata: dict
77
+
78
+ @classmethod
79
+ def format_query(cls, job_description: str, team_document_markdown: str) -> str:
80
+ if not isinstance(job_description, str):
81
+ raise ValueError("Invalid job_description.")
82
+ if not isinstance(team_document_markdown, str):
83
+ raise ValueError("Invalid team_document_markdown.")
84
+
85
+ query = (
86
+ f"Project description:\n{job_description}\n\n"
87
+ f"Document with team members:\n{team_document_markdown}"
88
+ )
89
+ return query
90
+
91
+ @classmethod
92
+ def execute(cls, llm: LLM, user_prompt: str) -> 'ReviewTeam':
93
+ """
94
+ Invoke LLM with the project description and team document to be reviewed.
95
+ """
96
+ if not isinstance(llm, LLM):
97
+ raise ValueError("Invalid LLM instance.")
98
+ if not isinstance(user_prompt, str):
99
+ raise ValueError("Invalid user_prompt.")
100
+
101
+ logger.debug(f"User Prompt:\n{user_prompt}")
102
+
103
+ system_prompt = REVIEW_TEAM_SYSTEM_PROMPT.strip()
104
+
105
+ chat_message_list = [
106
+ ChatMessage(
107
+ role=MessageRole.SYSTEM,
108
+ content=system_prompt,
109
+ ),
110
+ ChatMessage(
111
+ role=MessageRole.USER,
112
+ content=user_prompt,
113
+ )
114
+ ]
115
+
116
+ sllm = llm.as_structured_llm(DocumentDetails)
117
+ start_time = time.perf_counter()
118
+ try:
119
+ chat_response = sllm.chat(chat_message_list)
120
+ except Exception as e:
121
+ logger.debug(f"LLM chat interaction failed: {e}")
122
+ logger.error("LLM chat interaction failed.", exc_info=True)
123
+ raise ValueError("LLM chat interaction failed.") from e
124
+
125
+ end_time = time.perf_counter()
126
+ duration = int(ceil(end_time - start_time))
127
+ response_byte_count = len(chat_response.message.content.encode('utf-8'))
128
+ logger.info(f"LLM chat interaction completed in {duration} seconds. Response byte count: {response_byte_count}")
129
+
130
+ json_response = chat_response.raw.model_dump()
131
+
132
+ metadata = dict(llm.metadata)
133
+ metadata["llm_classname"] = llm.class_name()
134
+ metadata["duration"] = duration
135
+ metadata["response_byte_count"] = response_byte_count
136
+
137
+ result = ReviewTeam(
138
+ system_prompt=system_prompt,
139
+ user_prompt=user_prompt,
140
+ response=json_response,
141
+ metadata=metadata,
142
+ )
143
+ return result
144
+
145
+ def to_dict(self, include_metadata=True, include_system_prompt=True, include_user_prompt=True) -> dict:
146
+ d = self.response.copy()
147
+ if include_metadata:
148
+ d['metadata'] = self.metadata
149
+ if include_system_prompt:
150
+ d['system_prompt'] = self.system_prompt
151
+ if include_user_prompt:
152
+ d['user_prompt'] = self.user_prompt
153
+ return d
154
+
155
+ if __name__ == "__main__":
156
+ from src.llm_factory import get_llm
157
+
158
+ llm = get_llm("ollama-llama3.1")
159
+
160
+ path = os.path.join(os.path.dirname(__file__), 'test_data', "solarfarm_team_without_review.md")
161
+ with open(path, 'r', encoding='utf-8') as f:
162
+ team_document_markdown = f.read()
163
+ job_description = "Establish a solar farm in Denmark."
164
+
165
+ query = ReviewTeam.format_query(job_description, team_document_markdown)
166
+ print(f"Query:\n{query}\n\n")
167
+
168
+ review_team = ReviewTeam.execute(llm, query)
169
+ json_response = review_team.to_dict(include_system_prompt=False, include_user_prompt=False)
170
+ print(json.dumps(json_response, indent=2))
src/team/run_hire_team.py CHANGED
@@ -4,16 +4,18 @@ PROMPT> python -m src.team.run_hire_team
4
  from datetime import datetime
5
  import os
6
  import json
7
- from src.team.find_team_members import FIND_TEAM_MEMBERS_SYSTEM_PROMPT, find_team_members, cleanup_team_members_and_assign_id
8
- from src.team.enrich_team_members import ENRICH_TEAM_MEMBERS_SYSTEM_PROMPT, enrich_team_members, cleanup_enriched_team_members_and_merge_with_team_members
9
- from src.team.enrich_team_members_with_environment_info import ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_SYSTEM_PROMPT, enrich_team_members_with_environment_info, cleanup_enriched_team_members_with_environment_info_and_merge_with_team_members
10
- from src.team.create_markdown_document import create_markdown_document
 
 
11
  from src.plan.find_plan_prompt import find_plan_prompt
12
  from src.llm_factory import get_llm
13
 
14
  llm = get_llm("ollama-llama3.1")
15
  # llm = get_llm("openrouter-paid-gemini-2.0-flash-001")
16
- # llm = get_llm("deepseek-chat", max_tokens=8192)
17
 
18
  plan_prompt = find_plan_prompt("4dc34d55-0d0d-4e9d-92f4-23765f49dd29")
19
 
@@ -31,12 +33,12 @@ with open(plan_prompt_file, 'w') as f:
31
  f.write(plan_prompt)
32
 
33
  print("Finding team members for this task...")
34
- team_members_raw_dict = find_team_members(llm, plan_prompt, FIND_TEAM_MEMBERS_SYSTEM_PROMPT)
35
  team_members_raw_file = f'{run_dir}/002-team_members_raw.json'
36
  with open(team_members_raw_file, 'w') as f:
37
- f.write(json.dumps(team_members_raw_dict, indent=2))
38
 
39
- team_members_list = cleanup_team_members_and_assign_id(team_members_raw_dict)
40
  team_members_list_file = f'{run_dir}/003-team_members_list.json'
41
  with open(team_members_list_file, 'w') as f:
42
  f.write(json.dumps(team_members_list, indent=2))
@@ -45,31 +47,66 @@ print(f"Number of team members: {len(team_members_list)}")
45
  team_member_category_list = [item['category'] for item in team_members_list]
46
  print(f"Team member categories: {team_member_category_list}")
47
 
48
- print("Step A: Enriching team members...")
49
- enrich_team_members_raw_dict = enrich_team_members(llm, plan_prompt, team_members_list, ENRICH_TEAM_MEMBERS_SYSTEM_PROMPT)
50
- enrich_team_members_raw_file = f'{run_dir}/004-enrich_team_members_raw.json'
51
- with open(enrich_team_members_raw_file, 'w') as f:
52
- f.write(json.dumps(enrich_team_members_raw_dict, indent=2))
 
 
53
 
54
- enrich_team_members_list = cleanup_enriched_team_members_and_merge_with_team_members(enrich_team_members_raw_dict, team_members_list)
55
- enrich_team_members_list_file = f'{run_dir}/005-enriched_team_members_list.json'
56
- with open(enrich_team_members_list_file, 'w') as f:
57
- f.write(json.dumps(enrich_team_members_list, indent=2))
58
  print("Step A: Done enriching team members.")
59
 
60
- print("Step B: Enriching team members with environment info...")
61
- enrich_team_members_with_environment_info_raw_dict = enrich_team_members_with_environment_info(llm, plan_prompt, enrich_team_members_list, ENRICH_TEAM_MEMBERS_ENVIRONMENT_INFO_SYSTEM_PROMPT)
62
- enrich_team_members_with_environment_info_raw_file = f'{run_dir}/006-enrich_team_members_with_environment_info_raw.json'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  with open(enrich_team_members_with_environment_info_raw_file, 'w') as f:
64
  f.write(json.dumps(enrich_team_members_with_environment_info_raw_dict, indent=2))
65
 
66
- enrich_team_members_with_environment_info_list = cleanup_enriched_team_members_with_environment_info_and_merge_with_team_members(enrich_team_members_with_environment_info_raw_dict, enrich_team_members_list)
67
- enrich_team_members_with_environment_info_list_file = f'{run_dir}/007-enriched_team_members_with_environment_info_list.json'
68
  with open(enrich_team_members_with_environment_info_list_file, 'w') as f:
69
  f.write(json.dumps(enrich_team_members_with_environment_info_list, indent=2))
70
- print("Step B: Done enriching team members.")
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  print("Creating Markdown document...")
73
- output_file = f'{run_dir}/008-team.md'
74
- create_markdown_document(plan_prompt, enrich_team_members_with_environment_info_list_file, output_file)
 
 
 
 
 
 
75
  print("Done creating Markdown document.")
 
4
  from datetime import datetime
5
  import os
6
  import json
7
+ from src.team.find_team_members import FindTeamMembers
8
+ from src.team.enrich_team_members_with_contract_type import EnrichTeamMembersWithContractType
9
+ from src.team.enrich_team_members_with_background_story import EnrichTeamMembersWithBackgroundStory
10
+ from src.team.enrich_team_members_with_environment_info import EnrichTeamMembersWithEnvironmentInfo
11
+ from src.team.team_markdown_document import TeamMarkdownDocumentBuilder
12
+ from src.team.review_team import ReviewTeam
13
  from src.plan.find_plan_prompt import find_plan_prompt
14
  from src.llm_factory import get_llm
15
 
16
  llm = get_llm("ollama-llama3.1")
17
  # llm = get_llm("openrouter-paid-gemini-2.0-flash-001")
18
+ # llm = get_llm("openrouter-paid-openai-gpt-4o-mini")
19
 
20
  plan_prompt = find_plan_prompt("4dc34d55-0d0d-4e9d-92f4-23765f49dd29")
21
 
 
33
  f.write(plan_prompt)
34
 
35
  print("Finding team members for this task...")
36
+ find_team_members = FindTeamMembers.execute(llm, plan_prompt)
37
  team_members_raw_file = f'{run_dir}/002-team_members_raw.json'
38
  with open(team_members_raw_file, 'w') as f:
39
+ f.write(json.dumps(find_team_members.to_dict(), indent=2))
40
 
41
+ team_members_list = find_team_members.team_member_list
42
  team_members_list_file = f'{run_dir}/003-team_members_list.json'
43
  with open(team_members_list_file, 'w') as f:
44
  f.write(json.dumps(team_members_list, indent=2))
 
47
  team_member_category_list = [item['category'] for item in team_members_list]
48
  print(f"Team member categories: {team_member_category_list}")
49
 
50
+ print("Step A: Enriching team members with contract type...")
51
+ enrich_team_members_with_contract_type_query = EnrichTeamMembersWithContractType.format_query(plan_prompt, team_members_list)
52
+ enrich_team_members_with_contract_type = EnrichTeamMembersWithContractType.execute(llm, enrich_team_members_with_contract_type_query, team_members_list)
53
+ enrich_team_members_with_contract_type_raw_dict = enrich_team_members_with_contract_type.to_dict()
54
+ enrich_team_members_with_contract_type_raw_file = f'{run_dir}/004-enrich_team_members_with_contract_type_raw.json'
55
+ with open(enrich_team_members_with_contract_type_raw_file, 'w') as f:
56
+ f.write(json.dumps(enrich_team_members_with_contract_type_raw_dict, indent=2))
57
 
58
+ enrich_team_members_with_contract_type_list = enrich_team_members_with_contract_type.team_member_list
59
+ enrich_team_members_with_contract_type_list_file = f'{run_dir}/005-enrich_team_members_with_contract_type_list.json'
60
+ with open(enrich_team_members_with_contract_type_list_file, 'w') as f:
61
+ f.write(json.dumps(enrich_team_members_with_contract_type_list, indent=2))
62
  print("Step A: Done enriching team members.")
63
 
64
+ print("Step B: Enriching team members with background story...")
65
+ enrich_team_members_with_background_story_query = EnrichTeamMembersWithBackgroundStory.format_query(plan_prompt, enrich_team_members_with_contract_type_list)
66
+ enrich_team_members_with_background_story = EnrichTeamMembersWithBackgroundStory.execute(llm, enrich_team_members_with_background_story_query, enrich_team_members_with_contract_type_list)
67
+ enrich_team_members_with_background_story_raw_dict = enrich_team_members_with_background_story.to_dict()
68
+ enrich_team_members_with_background_story_raw_file = f'{run_dir}/006-enriched_team_members_with_background_story_raw.json'
69
+ with open(enrich_team_members_with_background_story_raw_file, 'w') as f:
70
+ f.write(json.dumps(enrich_team_members_with_background_story_raw_dict, indent=2))
71
+
72
+ enrich_team_members_with_background_story_list = enrich_team_members_with_background_story.team_member_list
73
+ enrich_team_members_with_background_story_list_file = f'{run_dir}/007-enrich_team_members_with_background_story_list.json'
74
+ with open(enrich_team_members_with_background_story_list_file, 'w') as f:
75
+ f.write(json.dumps(enrich_team_members_with_background_story_list, indent=2))
76
+ print("Step B: Done enriching team members.")
77
+
78
+ print("Step C: Enriching team members with environment info...")
79
+ enrich_team_members_with_environment_info_query = EnrichTeamMembersWithEnvironmentInfo.format_query(plan_prompt, enrich_team_members_with_background_story_list)
80
+ enrich_team_members_with_environment_info = EnrichTeamMembersWithEnvironmentInfo.execute(llm, enrich_team_members_with_environment_info_query, enrich_team_members_with_background_story_list)
81
+ enrich_team_members_with_environment_info_raw_dict = enrich_team_members_with_environment_info.to_dict()
82
+ enrich_team_members_with_environment_info_raw_file = f'{run_dir}/008-enrich_team_members_with_environment_info_raw.json'
83
  with open(enrich_team_members_with_environment_info_raw_file, 'w') as f:
84
  f.write(json.dumps(enrich_team_members_with_environment_info_raw_dict, indent=2))
85
 
86
+ enrich_team_members_with_environment_info_list = enrich_team_members_with_environment_info.team_member_list
87
+ enrich_team_members_with_environment_info_list_file = f'{run_dir}/009-enrich_team_members_with_environment_info_list.json'
88
  with open(enrich_team_members_with_environment_info_list_file, 'w') as f:
89
  f.write(json.dumps(enrich_team_members_with_environment_info_list, indent=2))
90
+ print("Step C: Done enriching team members.")
91
+
92
+ print("Step D: Reviewing team...")
93
+ builder1 = TeamMarkdownDocumentBuilder()
94
+ builder1.append_roles(enrich_team_members_with_environment_info_list, title=None)
95
+ review_team_query = ReviewTeam.format_query(plan_prompt, builder1.to_string())
96
+ review_team = ReviewTeam.execute(llm, review_team_query)
97
+ review_team_raw_dict = review_team.to_dict()
98
+ review_team_raw_file = f'{run_dir}/010-review_team_raw.json'
99
+ with open(review_team_raw_file, 'w') as f:
100
+ f.write(json.dumps(review_team_raw_dict, indent=2))
101
+ print("Step D: Reviewing team.")
102
 
103
  print("Creating Markdown document...")
104
+ builder2 = TeamMarkdownDocumentBuilder()
105
+ builder2.append_plan_prompt(plan_prompt)
106
+ builder2.append_separator()
107
+ builder2.append_roles(enrich_team_members_with_environment_info_list)
108
+ builder2.append_separator()
109
+ builder2.append_full_review(review_team.response)
110
+ output_file = f'{run_dir}/011-team.md'
111
+ builder2.write_to_file(output_file)
112
  print("Done creating Markdown document.")
src/team/team_markdown_document.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Create a Markdown document containing details about the team.
3
+
4
+ PROMPT> python -m src.team.team_markdown_document
5
+ """
6
+ import json
7
+ from typing import Optional
8
+
9
+ class TeamMarkdownDocumentBuilder:
10
+ """
11
+ A class to build a Markdown document containing details about the team.
12
+ """
13
+ def __init__(self):
14
+ self.rows = []
15
+
16
+ def append_separator(self):
17
+ self.rows.append("\n---\n")
18
+
19
+ def append_plan_prompt(self, plan_prompt: str):
20
+ """The main topic text to include in the Markdown"""
21
+ self.rows.append("# The plan\n")
22
+ self.rows.append(plan_prompt.strip())
23
+
24
+ def append_role(self, entry: dict, role_index: int):
25
+ self.rows.append(f"\n## {role_index}. {entry['category']}")
26
+ if 'contract_type' in entry:
27
+ self.rows.append(f"\n**Contract Type**: `{entry['contract_type']}`")
28
+ if 'contract_type_justification' in entry:
29
+ self.rows.append(f"\n**Contract Type Justification**: {entry['contract_type_justification']}")
30
+ if 'explanation' in entry:
31
+ self.rows.append(f"\n**Explanation**:\n{entry['explanation']}")
32
+ if 'consequences' in entry:
33
+ self.rows.append(f"\n**Consequences**:\n{entry['consequences']}")
34
+ if 'count' in entry:
35
+ self.rows.append(f"\n**People Count**:\n{entry['count']}")
36
+ if 'typical_job_activities' in entry:
37
+ self.rows.append(f"\n**Typical Activities**:\n{entry['typical_job_activities']}")
38
+ if 'background_story' in entry:
39
+ self.rows.append(f"\n**Background Story**:\n{entry['background_story']}")
40
+ if 'equipment_needs' in entry:
41
+ self.rows.append(f"\n**Equipment Needs**:\n{entry['equipment_needs']}")
42
+ if 'facility_needs' in entry:
43
+ self.rows.append(f"\n**Facility Needs**:\n{entry['facility_needs']}")
44
+
45
+ def append_roles(self, roles_list: list[dict], title: Optional[str] = "Roles"):
46
+ if isinstance(title, str):
47
+ self.rows.append(f"# {title}")
48
+ for entry_index, entry in enumerate(roles_list, start=1):
49
+ self.append_role(entry, entry_index)
50
+
51
+ def append_review_item(self, review_item: dict, review_index: int):
52
+ issue = review_item.get('issue', "Missing Review Issue")
53
+ self.rows.append(f"\n## {review_index}. {issue}")
54
+ if 'explanation' in review_item:
55
+ self.rows.append(f"\n{review_item['explanation']}")
56
+ if 'recommendation' in review_item:
57
+ self.rows.append(f"\n**Recommendation**:\n{review_item['recommendation']}")
58
+
59
+ def append_review_items(self, review_items: list[dict], title: Optional[str] = "Review Items"):
60
+ if isinstance(title, str):
61
+ self.rows.append(f"# {title}")
62
+ for review_index, review_item in enumerate(review_items, start=1):
63
+ self.append_review_item(review_item, review_index)
64
+
65
+ def append_full_review(self, review: dict):
66
+ review_omissions = review.get('omissions', [])
67
+ self.append_review_items(review_omissions, title="Omissions")
68
+ self.append_separator()
69
+ review_potential_improvements = review.get('potential_improvements', [])
70
+ self.append_review_items(review_potential_improvements, title="Potential Improvements")
71
+
72
+ def to_string(self) -> str:
73
+ return "\n".join(self.rows)
74
+
75
+ def write_to_file(self, output_file_path: str):
76
+ markdown_representation = self.to_string()
77
+ with open(output_file_path, 'w', encoding='utf-8') as out_f:
78
+ out_f.write(markdown_representation)
79
+
80
+ if __name__ == "__main__":
81
+ import os
82
+
83
+ plan_prompt = "Establish a solar farm in Denmark."
84
+
85
+ path1 = os.path.join(os.path.dirname(__file__), 'test_data', "solarfarm_roles_list.json")
86
+ with open(path1, 'r', encoding='utf-8') as f:
87
+ roles_list = json.load(f)
88
+
89
+ path2 = os.path.join(os.path.dirname(__file__), 'test_data', "solarfarm_team_review.json")
90
+ with open(path2, 'r', encoding='utf-8') as f:
91
+ team_review = json.load(f)
92
+
93
+ builder2 = TeamMarkdownDocumentBuilder()
94
+ builder2.append_plan_prompt(plan_prompt)
95
+ builder2.append_separator()
96
+ builder2.append_roles(roles_list)
97
+ builder2.append_separator()
98
+ builder2.append_full_review(team_review)
99
+
100
+ print(builder2.to_string())
src/team/test_data/solarfarm_roles_list.json ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": 1,
4
+ "category": "Project Manager",
5
+ "explanation": "Oversees the entire project, ensuring it stays on schedule and within budget.",
6
+ "consequences": "Without a project manager, the project may lack direction, leading to delays and budget overruns.",
7
+ "count": "1",
8
+ "contract_type": "full_time_employee",
9
+ "contract_type_justification": "The Project Manager needs to be fully integrated into the project to ensure consistent oversight and decision-making throughout the project's duration.",
10
+ "typical_job_activities": "Coordinate project schedules, manage budgets, liaise with stakeholders, oversee project execution, and ensure compliance with regulations.",
11
+ "background_story": "Lars Jensen, a seasoned project manager based in Aarhus, Denmark, graduated with a Master's degree in Project Management from the University of Aarhus. With over 10 years of experience in managing renewable energy projects, Lars has successfully overseen the construction of multiple wind and solar farms across Scandinavia. His strong leadership skills and ability to navigate complex regulatory environments make him an invaluable asset to the team. Lars is well-versed in the intricacies of project timelines and budgets, ensuring that the solar farm project remains on track and within financial constraints.",
12
+ "equipment_needs": "Laptop with project management software, communication tools, and access to project documentation.",
13
+ "facility_needs": "Office space for meetings and collaboration with team members and stakeholders."
14
+ },
15
+ {
16
+ "id": 2,
17
+ "category": "Site Engineer",
18
+ "explanation": "Responsible for the technical aspects of the solar farm's construction and installation.",
19
+ "consequences": "Absence of a site engineer can result in construction errors and safety issues, jeopardizing the project.",
20
+ "count": "2",
21
+ "contract_type": "full_time_employee",
22
+ "contract_type_justification": "The Site Engineer is essential for the technical execution of the project and requires full-time commitment to address ongoing construction challenges.",
23
+ "typical_job_activities": "Oversee construction activities, ensure compliance with engineering specifications, conduct site inspections, and collaborate with contractors.",
24
+ "background_story": "Sophie Madsen, a site engineer from Odense, Denmark, holds a Bachelor's degree in Civil Engineering from the Technical University of Denmark. With five years of hands-on experience in solar farm construction, Sophie has developed a keen eye for detail and a strong understanding of the technical requirements involved in such projects. Her familiarity with construction processes and safety regulations makes her a critical member of the team, ensuring that the solar farm is built to the highest standards.",
25
+ "equipment_needs": "Construction tools, safety gear, surveying equipment, and a laptop with engineering software.",
26
+ "facility_needs": "Access to the construction site and a temporary office or trailer for on-site coordination."
27
+ },
28
+ {
29
+ "id": 3,
30
+ "category": "Environmental Consultant",
31
+ "explanation": "Assesses the environmental impact of the solar farm and ensures compliance with regulations.",
32
+ "consequences": "Failure to address environmental concerns can lead to legal issues and project delays.",
33
+ "count": "1",
34
+ "contract_type": "independent_contractor",
35
+ "contract_type_justification": "The Environmental Consultant can be engaged on a project basis, providing expertise as needed without the requirement for full-time employment.",
36
+ "typical_job_activities": "Conduct environmental impact assessments, prepare reports, liaise with regulatory bodies, and recommend mitigation strategies.",
37
+ "background_story": "Freja S\u00f8rensen, an environmental consultant based in Copenhagen, Denmark, earned her Master's degree in Environmental Science from the University of Copenhagen. With over eight years of experience in environmental assessments for renewable energy projects, Freja is adept at identifying potential ecological impacts and ensuring compliance with local regulations. Her expertise is crucial for the solar farm project, as she will help navigate environmental challenges and secure necessary permits.",
38
+ "equipment_needs": "Laptop with environmental assessment software, access to environmental databases, and reporting tools.",
39
+ "facility_needs": "Office space for conducting assessments and meetings with regulatory bodies."
40
+ },
41
+ {
42
+ "id": 4,
43
+ "category": "Financial Analyst",
44
+ "explanation": "Evaluates the financial feasibility of the project and manages funding and budgeting.",
45
+ "consequences": "Without financial oversight, the project may face funding shortages or misallocation of resources.",
46
+ "count": "1",
47
+ "contract_type": "full_time_employee",
48
+ "contract_type_justification": "The Financial Analyst plays a critical role in managing the project's finances and requires continuous involvement to ensure financial health.",
49
+ "typical_job_activities": "Evaluate financial models, manage budgets, analyze funding options, and prepare financial reports.",
50
+ "background_story": "Mikkel Thomsen, a financial analyst from Aalborg, Denmark, graduated with a degree in Finance from Aalborg University. With a background in renewable energy financing, Mikkel has spent the last six years analyzing project feasibility and managing budgets for various energy initiatives. His analytical skills and financial acumen are essential for ensuring that the solar farm project remains financially viable and well-funded.",
51
+ "equipment_needs": "Laptop with financial modeling software, access to financial databases, and reporting tools.",
52
+ "facility_needs": "Office space for financial analysis and meetings with project stakeholders."
53
+ },
54
+ {
55
+ "id": 5,
56
+ "category": "Procurement Specialist",
57
+ "explanation": "Handles the sourcing and purchasing of materials and equipment needed for the solar farm.",
58
+ "consequences": "Lack of procurement expertise can lead to delays in acquiring necessary materials, affecting the project timeline.",
59
+ "count": "1",
60
+ "contract_type": "independent_contractor",
61
+ "contract_type_justification": "The Procurement Specialist can be contracted for specific phases of the project, focusing on sourcing materials without needing a full-time position.",
62
+ "typical_job_activities": "Source and negotiate with suppliers, manage purchase orders, track inventory, and ensure timely delivery of materials.",
63
+ "background_story": "Clara Nielsen, a procurement specialist located in Esbjerg, Denmark, has a degree in Supply Chain Management from the University of Southern Denmark. With over seven years of experience in sourcing materials for construction projects, Clara has developed strong negotiation skills and a vast network of suppliers. Her role is vital for ensuring that the solar farm has all necessary materials and equipment on time and within budget.",
64
+ "equipment_needs": "Laptop with procurement management software, access to supplier databases, and communication tools.",
65
+ "facility_needs": "Office space for sourcing materials and negotiating with suppliers."
66
+ },
67
+ {
68
+ "id": 6,
69
+ "category": "Construction Crew",
70
+ "explanation": "A team of workers responsible for the physical construction of the solar farm.",
71
+ "consequences": "Insufficient workforce can slow down construction progress and lead to missed deadlines.",
72
+ "count": "min 5, max 10, depending on project scale",
73
+ "contract_type": "agency_temp",
74
+ "contract_type_justification": "The Construction Crew can be sourced through an agency to provide flexibility in workforce size based on project demands and timelines.",
75
+ "typical_job_activities": "Install solar panels, perform site preparation, conduct safety checks, and collaborate with site engineers.",
76
+ "background_story": "The construction crew, led by foreman Jakob Larsen, is a diverse team of skilled workers from various backgrounds in construction and renewable energy. Based in different regions of Denmark, the crew members have undergone specialized training in solar panel installation and safety protocols. Their collective experience and teamwork are essential for the successful physical construction of the solar farm, ensuring that all tasks are completed efficiently and safely.",
77
+ "equipment_needs": "Construction tools, safety gear, and equipment for solar panel installation.",
78
+ "facility_needs": "Access to the construction site and temporary facilities for the crew."
79
+ },
80
+ {
81
+ "id": 7,
82
+ "category": "Quality Assurance Specialist",
83
+ "explanation": "Ensures that all work meets the required standards and specifications.",
84
+ "consequences": "Without quality assurance, the project may face issues with system performance and reliability.",
85
+ "count": "1",
86
+ "contract_type": "part_time_employee",
87
+ "contract_type_justification": "The Quality Assurance Specialist may not need to be full-time, as their role can be fulfilled on a part-time basis during critical phases of construction.",
88
+ "typical_job_activities": "Conduct quality inspections, develop quality assurance protocols, document findings, and collaborate with construction teams.",
89
+ "background_story": "Anna Pedersen, a quality assurance specialist from Randers, Denmark, holds a degree in Quality Management from the University of Aarhus. With over four years of experience in quality control within the renewable energy sector, Anna is skilled in developing and implementing quality assurance protocols. Her attention to detail and commitment to excellence are crucial for ensuring that the solar farm meets all required standards and specifications.",
90
+ "equipment_needs": "Laptop with quality management software, inspection tools, and documentation materials.",
91
+ "facility_needs": "Office space for conducting quality inspections and meetings with construction teams."
92
+ },
93
+ {
94
+ "id": 8,
95
+ "category": "Maintenance Technician",
96
+ "explanation": "Responsible for the ongoing maintenance and troubleshooting of the solar farm post-construction.",
97
+ "consequences": "Lack of maintenance can lead to system failures and decreased efficiency over time.",
98
+ "count": "min 1, max 3, depending on project scale and operational needs",
99
+ "contract_type": "independent_contractor",
100
+ "contract_type_justification": "The Maintenance Technician can be contracted as needed post-construction, allowing for flexibility in scheduling maintenance activities.",
101
+ "typical_job_activities": "Perform routine maintenance, troubleshoot system issues, conduct performance assessments, and coordinate with the project team for repairs.",
102
+ "background_story": "Oliver Kristensen, a maintenance technician based in Kolding, Denmark, has a background in electrical engineering from the University of Southern Denmark. With three years of experience in maintaining solar energy systems, Oliver is adept at troubleshooting and performing routine maintenance to ensure optimal performance. His role post-construction is vital for the long-term efficiency and reliability of the solar farm.",
103
+ "equipment_needs": "Maintenance tools, diagnostic equipment, and a laptop for performance assessments.",
104
+ "facility_needs": "Access to the solar farm for maintenance activities and a workshop for repairs."
105
+ }
106
+ ]
src/team/test_data/solarfarm_team_review.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "omissions": [
3
+ {
4
+ "issue": "Community Engagement Coordinator",
5
+ "explanation": "Engaging with the local community is crucial for gaining support and addressing concerns related to the solar farm project.",
6
+ "recommendation": "Consider appointing a Community Engagement Coordinator to facilitate communication with local residents, address their concerns, and promote the benefits of the solar farm."
7
+ },
8
+ {
9
+ "issue": "Health and Safety Officer",
10
+ "explanation": "A dedicated Health and Safety Officer is essential to ensure compliance with safety regulations and to promote a safe working environment on-site.",
11
+ "recommendation": "Introduce a Health and Safety Officer role to oversee safety protocols, conduct training, and ensure that all safety measures are adhered to during construction."
12
+ },
13
+ {
14
+ "issue": "Legal Advisor",
15
+ "explanation": "Legal expertise is necessary to navigate contracts, permits, and compliance with local laws and regulations.",
16
+ "recommendation": "Engage a Legal Advisor on a consulting basis to review contracts, assist with regulatory compliance, and address any legal issues that may arise during the project."
17
+ }
18
+ ],
19
+ "potential_improvements": [
20
+ {
21
+ "issue": "Clarification of Roles",
22
+ "explanation": "Some roles may have overlapping responsibilities, which can lead to confusion and inefficiencies.",
23
+ "recommendation": "Clearly define the responsibilities of each team member, particularly between the Site Engineer and the Quality Assurance Specialist, to ensure accountability and streamline processes."
24
+ },
25
+ {
26
+ "issue": "Regular Team Meetings",
27
+ "explanation": "Frequent communication among team members is vital for project alignment and addressing issues promptly.",
28
+ "recommendation": "Implement regular team meetings to discuss progress, challenges, and updates, ensuring that all team members are informed and engaged."
29
+ },
30
+ {
31
+ "issue": "Documentation and Reporting",
32
+ "explanation": "Proper documentation is essential for tracking progress and ensuring compliance with regulations.",
33
+ "recommendation": "Establish a centralized documentation system where all team members can access and update project-related documents, ensuring transparency and accountability."
34
+ }
35
+ ],
36
+ "metadata": {
37
+ "context_window": 3900,
38
+ "num_output": 8192,
39
+ "is_chat_model": true,
40
+ "is_function_calling_model": false,
41
+ "model_name": "openai/gpt-4o-mini",
42
+ "system_role": "system",
43
+ "llm_classname": "OpenRouter_LLM",
44
+ "duration": 10,
45
+ "response_byte_count": 2130
46
+ },
47
+ "system_prompt": "You are an expert in designing and evaluating team structures for projects of all scales\u2014from personal or trivial endeavors to large, complex initiatives. Your task is to review a team document that includes a project plan, detailed team roles, and sections on omissions and potential improvements.\n\nIn your analysis, please:\n\n1. **Review the Team Composition:**\n - Examine the team roles described, including details such as contract types, typical activities, background stories, and resource needs.\n - Consider whether the roles sufficiently cover all aspects of the project given its scope.\n\n2. **Identify Omissions:**\n - Highlight any significant missing roles, support functions, or expertise areas that are critical for the project's success.\n - **Important:** When the project is personal or trivial, avoid suggesting overly formal or business-oriented roles (e.g., Marketing Specialist, Legal Advisor, Technical Support Specialist). Instead, suggest simpler or integrative adjustments suitable for a personal context.\n\n3. **Suggest Potential Improvements:**\n - Recommend actionable changes that enhance the team's overall effectiveness, communication, and clarity.\n - Focus on clarifying responsibilities and reducing overlap.\n - For personal or non-commercial projects, tailor your recommendations to be straightforward and avoid introducing new formal roles that are unnecessary.\n\n4. **Provide Actionable Recommendations:**\n - For each identified omission or improvement, offer specific, practical advice on how to address the issue.\n - Ensure your recommendations are scaled appropriately to the project's nature.\n\nYour output must be a JSON object with two top-level keys: \"omissions\" and \"potential_improvements\". Each key should map to an array of objects, where each object contains:\n- `\"issue\"`: A brief title summarizing the omission or improvement.\n- `\"explanation\"`: A concise explanation of why this issue is significant in relation to the project's goals.\n- `\"recommendation\"`: Specific, actionable advice on how to address the issue.\n\nEnsure your JSON output strictly follows this structure without any additional commentary or text.",
48
+ "user_prompt": "Project description:\nEstablish a solar farm in Denmark.\n\nDocument with team members:\n\n## 1. Project Manager\n\n**Contract Type**: `full_time_employee`\n\n**Contract Type Justification**: The Project Manager needs to be fully integrated into the project to ensure consistent oversight and decision-making throughout the project's duration.\n\n**Explanation**:\nOversees the entire project, ensuring it stays on schedule and within budget.\n\n**Consequences**:\nWithout a project manager, the project may lack direction, leading to delays and budget overruns.\n\n**People Count**:\n1\n\n**Typical Activities**:\nCoordinate project schedules, manage budgets, liaise with stakeholders, oversee project execution, and ensure compliance with regulations.\n\n**Background Story**:\nLars Jensen, a seasoned project manager based in Aarhus, Denmark, graduated with a Master's degree in Project Management from the University of Aarhus. With over 10 years of experience in managing renewable energy projects, Lars has successfully overseen the construction of multiple wind and solar farms across Scandinavia. His strong leadership skills and ability to navigate complex regulatory environments make him an invaluable asset to the team. Lars is well-versed in the intricacies of project timelines and budgets, ensuring that the solar farm project remains on track and within financial constraints.\n\n**Equipment Needs**:\nLaptop with project management software, communication tools, and access to project documentation.\n\n**Facility Needs**:\nOffice space for meetings and collaboration with team members and stakeholders.\n\n## 2. Site Engineer\n\n**Contract Type**: `full_time_employee`\n\n**Contract Type Justification**: The Site Engineer is essential for the technical execution of the project and requires full-time commitment to address ongoing construction challenges.\n\n**Explanation**:\nResponsible for the technical aspects of the solar farm's construction and installation.\n\n**Consequences**:\nAbsence of a site engineer can result in construction errors and safety issues, jeopardizing the project.\n\n**People Count**:\n2\n\n**Typical Activities**:\nOversee construction activities, ensure compliance with engineering specifications, conduct site inspections, and collaborate with contractors.\n\n**Background Story**:\nSophie Madsen, a site engineer from Odense, Denmark, holds a Bachelor's degree in Civil Engineering from the Technical University of Denmark. With five years of hands-on experience in solar farm construction, Sophie has developed a keen eye for detail and a strong understanding of the technical requirements involved in such projects. Her familiarity with construction processes and safety regulations makes her a critical member of the team, ensuring that the solar farm is built to the highest standards.\n\n**Equipment Needs**:\nConstruction tools, safety gear, surveying equipment, and a laptop with engineering software.\n\n**Facility Needs**:\nAccess to the construction site and a temporary office or trailer for on-site coordination.\n\n## 3. Environmental Consultant\n\n**Contract Type**: `independent_contractor`\n\n**Contract Type Justification**: The Environmental Consultant can be engaged on a project basis, providing expertise as needed without the requirement for full-time employment.\n\n**Explanation**:\nAssesses the environmental impact of the solar farm and ensures compliance with regulations.\n\n**Consequences**:\nFailure to address environmental concerns can lead to legal issues and project delays.\n\n**People Count**:\n1\n\n**Typical Activities**:\nConduct environmental impact assessments, prepare reports, liaise with regulatory bodies, and recommend mitigation strategies.\n\n**Background Story**:\nFreja S\u00f8rensen, an environmental consultant based in Copenhagen, Denmark, earned her Master's degree in Environmental Science from the University of Copenhagen. With over eight years of experience in environmental assessments for renewable energy projects, Freja is adept at identifying potential ecological impacts and ensuring compliance with local regulations. Her expertise is crucial for the solar farm project, as she will help navigate environmental challenges and secure necessary permits.\n\n**Equipment Needs**:\nLaptop with environmental assessment software, access to environmental databases, and reporting tools.\n\n**Facility Needs**:\nOffice space for conducting assessments and meetings with regulatory bodies.\n\n## 4. Financial Analyst\n\n**Contract Type**: `full_time_employee`\n\n**Contract Type Justification**: The Financial Analyst plays a critical role in managing the project's finances and requires continuous involvement to ensure financial health.\n\n**Explanation**:\nEvaluates the financial feasibility of the project and manages funding and budgeting.\n\n**Consequences**:\nWithout financial oversight, the project may face funding shortages or misallocation of resources.\n\n**People Count**:\n1\n\n**Typical Activities**:\nEvaluate financial models, manage budgets, analyze funding options, and prepare financial reports.\n\n**Background Story**:\nMikkel Thomsen, a financial analyst from Aalborg, Denmark, graduated with a degree in Finance from Aalborg University. With a background in renewable energy financing, Mikkel has spent the last six years analyzing project feasibility and managing budgets for various energy initiatives. His analytical skills and financial acumen are essential for ensuring that the solar farm project remains financially viable and well-funded.\n\n**Equipment Needs**:\nLaptop with financial modeling software, access to financial databases, and reporting tools.\n\n**Facility Needs**:\nOffice space for financial analysis and meetings with project stakeholders.\n\n## 5. Procurement Specialist\n\n**Contract Type**: `independent_contractor`\n\n**Contract Type Justification**: The Procurement Specialist can be contracted for specific phases of the project, focusing on sourcing materials without needing a full-time position.\n\n**Explanation**:\nHandles the sourcing and purchasing of materials and equipment needed for the solar farm.\n\n**Consequences**:\nLack of procurement expertise can lead to delays in acquiring necessary materials, affecting the project timeline.\n\n**People Count**:\n1\n\n**Typical Activities**:\nSource and negotiate with suppliers, manage purchase orders, track inventory, and ensure timely delivery of materials.\n\n**Background Story**:\nClara Nielsen, a procurement specialist located in Esbjerg, Denmark, has a degree in Supply Chain Management from the University of Southern Denmark. With over seven years of experience in sourcing materials for construction projects, Clara has developed strong negotiation skills and a vast network of suppliers. Her role is vital for ensuring that the solar farm has all necessary materials and equipment on time and within budget.\n\n**Equipment Needs**:\nLaptop with procurement management software, access to supplier databases, and communication tools.\n\n**Facility Needs**:\nOffice space for sourcing materials and negotiating with suppliers.\n\n## 6. Construction Crew\n\n**Contract Type**: `agency_temp`\n\n**Contract Type Justification**: The Construction Crew can be sourced through an agency to provide flexibility in workforce size based on project demands and timelines.\n\n**Explanation**:\nA team of workers responsible for the physical construction of the solar farm.\n\n**Consequences**:\nInsufficient workforce can slow down construction progress and lead to missed deadlines.\n\n**People Count**:\nmin 5, max 10, depending on project scale\n\n**Typical Activities**:\nInstall solar panels, perform site preparation, conduct safety checks, and collaborate with site engineers.\n\n**Background Story**:\nThe construction crew, led by foreman Jakob Larsen, is a diverse team of skilled workers from various backgrounds in construction and renewable energy. Based in different regions of Denmark, the crew members have undergone specialized training in solar panel installation and safety protocols. Their collective experience and teamwork are essential for the successful physical construction of the solar farm, ensuring that all tasks are completed efficiently and safely.\n\n**Equipment Needs**:\nConstruction tools, safety gear, and equipment for solar panel installation.\n\n**Facility Needs**:\nAccess to the construction site and temporary facilities for the crew.\n\n## 7. Quality Assurance Specialist\n\n**Contract Type**: `part_time_employee`\n\n**Contract Type Justification**: The Quality Assurance Specialist may not need to be full-time, as their role can be fulfilled on a part-time basis during critical phases of construction.\n\n**Explanation**:\nEnsures that all work meets the required standards and specifications.\n\n**Consequences**:\nWithout quality assurance, the project may face issues with system performance and reliability.\n\n**People Count**:\n1\n\n**Typical Activities**:\nConduct quality inspections, develop quality assurance protocols, document findings, and collaborate with construction teams.\n\n**Background Story**:\nAnna Pedersen, a quality assurance specialist from Randers, Denmark, holds a degree in Quality Management from the University of Aarhus. With over four years of experience in quality control within the renewable energy sector, Anna is skilled in developing and implementing quality assurance protocols. Her attention to detail and commitment to excellence are crucial for ensuring that the solar farm meets all required standards and specifications.\n\n**Equipment Needs**:\nLaptop with quality management software, inspection tools, and documentation materials.\n\n**Facility Needs**:\nOffice space for conducting quality inspections and meetings with construction teams.\n\n## 8. Maintenance Technician\n\n**Contract Type**: `independent_contractor`\n\n**Contract Type Justification**: The Maintenance Technician can be contracted as needed post-construction, allowing for flexibility in scheduling maintenance activities.\n\n**Explanation**:\nResponsible for the ongoing maintenance and troubleshooting of the solar farm post-construction.\n\n**Consequences**:\nLack of maintenance can lead to system failures and decreased efficiency over time.\n\n**People Count**:\nmin 1, max 3, depending on project scale and operational needs\n\n**Typical Activities**:\nPerform routine maintenance, troubleshoot system issues, conduct performance assessments, and coordinate with the project team for repairs.\n\n**Background Story**:\nOliver Kristensen, a maintenance technician based in Kolding, Denmark, has a background in electrical engineering from the University of Southern Denmark. With three years of experience in maintaining solar energy systems, Oliver is adept at troubleshooting and performing routine maintenance to ensure optimal performance. His role post-construction is vital for the long-term efficiency and reliability of the solar farm.\n\n**Equipment Needs**:\nMaintenance tools, diagnostic equipment, and a laptop for performance assessments.\n\n**Facility Needs**:\nAccess to the solar farm for maintenance activities and a workshop for repairs."
49
+ }
src/team/test_data/solarfarm_team_without_review.md ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Role 1 - Project Manager
2
+
3
+ **Contract Type**: full_time_employee
4
+
5
+ **Contract Type Justification**: The Project Manager requires consistent involvement and oversight throughout the project lifecycle, making a full-time employee the most suitable option.
6
+
7
+ **Explanation**:
8
+ Oversees all aspects of the solar farm project, ensuring it stays on schedule and within budget. Manages communication between different teams and stakeholders.
9
+
10
+ **Consequences**:
11
+ Project delays, cost overruns, and miscommunication between teams.
12
+
13
+ **People Count**:
14
+ 1
15
+
16
+ **Typical Activities**:
17
+ Developing project plans, managing budgets, coordinating teams, communicating with stakeholders, and ensuring project milestones are met on time and within budget.
18
+
19
+ **Background Story**:
20
+ Astrid Nielsen, a seasoned project manager from Copenhagen, has a proven track record of successfully delivering large-scale infrastructure projects across Denmark. With a Master's degree in Engineering Management from the Technical University of Denmark and over 15 years of experience in the construction and renewable energy sectors, Astrid possesses a comprehensive understanding of project lifecycles, risk management, and stakeholder communication. She has previously managed the construction of a large wind farm in Jutland, giving her direct experience with the challenges and opportunities of renewable energy projects in the Danish landscape. Astrid's meticulous planning, proactive problem-solving skills, and ability to foster collaboration make her the ideal candidate to lead the solar farm project.
21
+
22
+ **Equipment Needs**:
23
+ Laptop, project management software, communication tools (phone, video conferencing).
24
+
25
+ **Facility Needs**:
26
+ Office space, meeting rooms.
27
+
28
+ ---
29
+
30
+ ## Role 2 - Solar Energy Engineer
31
+
32
+ **Contract Type**: full_time_employee
33
+
34
+ **Contract Type Justification**: The Solar Energy Engineer's role is critical for the entire project and requires in-depth knowledge of the project specifics. A full-time employee ensures consistent involvement and expertise.
35
+
36
+ **Explanation**:
37
+ Designs the solar farm layout, selects appropriate solar panel technology, and optimizes energy production based on the Danish climate and grid infrastructure.
38
+
39
+ **Consequences**:
40
+ Inefficient energy production, suboptimal panel selection, and potential grid integration issues.
41
+
42
+ **People Count**:
43
+ 1
44
+
45
+ **Typical Activities**:
46
+ Designing solar farm layouts, selecting solar panel technology, optimizing energy production, conducting energy yield assessments, and ensuring grid integration compliance.
47
+
48
+ **Background Story**:
49
+ Erik Hansen, a highly skilled Solar Energy Engineer hailing from Aarhus, possesses a deep understanding of photovoltaic systems and renewable energy technologies. He holds a PhD in Solar Energy Engineering from Aarhus University and has spent the last decade designing and optimizing solar energy systems for various applications, including residential, commercial, and utility-scale projects. Erik's expertise lies in solar panel technology selection, energy yield modeling, and grid integration strategies. He is intimately familiar with the Danish climate and grid infrastructure, having previously worked on several solar energy projects in the region. Erik's analytical skills, technical expertise, and passion for renewable energy make him an invaluable asset to the solar farm project.
50
+
51
+ **Equipment Needs**:
52
+ High-performance computer, specialized solar design software (e.g., PVsyst, Helioscope), data analysis tools.
53
+
54
+ **Facility Needs**:
55
+ Office space, access to meteorological data and grid information.
56
+
57
+ ---
58
+
59
+ ## Role 3 - Environmental Impact Assessor
60
+
61
+ **Contract Type**: independent_contractor
62
+
63
+ **Contract Type Justification**: The Environmental Impact Assessor's role is project-specific and requires specialized expertise. An independent contractor can provide this expertise on a project basis.
64
+
65
+ **Explanation**:
66
+ Conducts a thorough environmental impact assessment to identify and mitigate potential negative effects on the local ecosystem, ensuring compliance with Danish environmental regulations.
67
+
68
+ **Consequences**:
69
+ Regulatory delays, potential environmental damage, and negative public perception.
70
+
71
+ **People Count**:
72
+ 1
73
+
74
+ **Typical Activities**:
75
+ Conducting environmental impact assessments, identifying potential environmental impacts, developing mitigation strategies, preparing environmental reports, and ensuring compliance with environmental regulations.
76
+
77
+ **Background Story**:
78
+ Birgitte Christensen, an independent Environmental Impact Assessor based in Odense, has extensive experience in evaluating the environmental consequences of development projects in Denmark. With a Master's degree in Environmental Science from the University of Southern Denmark and over 8 years of experience in environmental consulting, Birgitte has conducted numerous environmental impact assessments for a wide range of projects, including wind farms, infrastructure developments, and industrial facilities. She is intimately familiar with Danish environmental regulations and best practices for mitigating environmental impacts. Birgitte's thoroughness, attention to detail, and commitment to environmental sustainability make her the perfect choice to assess the environmental impact of the solar farm project.
79
+
80
+ **Equipment Needs**:
81
+ Field equipment for environmental sampling and monitoring, data analysis software, report writing software.
82
+
83
+ **Facility Needs**:
84
+ Access to environmental databases, laboratory access (if required for sample analysis), transportation to site.
85
+
86
+ ---
87
+
88
+ ## Role 4 - Land Acquisition Specialist
89
+
90
+ **Contract Type**: independent_contractor
91
+
92
+ **Contract Type Justification**: The Land Acquisition Specialist's role is specific to the initial phase of the project. An independent contractor with local expertise is well-suited for this task.
93
+
94
+ **Explanation**:
95
+ Identifies and secures suitable land for the solar farm, negotiating purchase or lease agreements with landowners, and navigating Danish land use regulations.
96
+
97
+ **Consequences**:
98
+ Difficulty finding suitable land, delays in project commencement, and increased project costs.
99
+
100
+ **People Count**:
101
+ 1
102
+
103
+ **Typical Activities**:
104
+ Identifying suitable land for the solar farm, negotiating purchase or lease agreements with landowners, conducting due diligence on land parcels, and navigating Danish land use regulations.
105
+
106
+ **Background Story**:
107
+ Jens Petersen, a seasoned Land Acquisition Specialist from Aalborg, has a proven track record of successfully securing land for development projects across Denmark. With over 12 years of experience in real estate and land acquisition, Jens possesses a deep understanding of Danish land use regulations, property valuation, and negotiation strategies. He has previously worked on several renewable energy projects, including wind farms and biomass plants, giving him direct experience with the challenges and opportunities of land acquisition in the renewable energy sector. Jens's local knowledge, negotiation skills, and ability to build relationships with landowners make him the ideal candidate to secure suitable land for the solar farm project.
108
+
109
+ **Equipment Needs**:
110
+ Transportation, access to land registry databases, GIS software, negotiation tools.
111
+
112
+ **Facility Needs**:
113
+ Office space (potentially remote), access to legal resources.
114
+
115
+ ---
116
+
117
+ ## Role 5 - Grid Connection Specialist
118
+
119
+ **Contract Type**: independent_contractor
120
+
121
+ **Contract Type Justification**: The Grid Connection Specialist requires specialized knowledge of the Danish power grid and regulations. An independent contractor with relevant experience is a good fit.
122
+
123
+ **Explanation**:
124
+ Manages the interconnection of the solar farm to the Danish power grid, ensuring compliance with grid operator requirements and optimizing energy delivery.
125
+
126
+ **Consequences**:
127
+ Delays in grid connection, potential energy losses, and non-compliance with grid regulations.
128
+
129
+ **People Count**:
130
+ 1
131
+
132
+ **Typical Activities**:
133
+ Managing the interconnection of the solar farm to the Danish power grid, ensuring compliance with grid operator requirements, optimizing energy delivery, and conducting power system studies.
134
+
135
+ **Background Story**:
136
+ Lars Jensen, an independent Grid Connection Specialist based in Esbjerg, has extensive experience in managing the interconnection of renewable energy projects to the Danish power grid. With a Master's degree in Electrical Engineering from the University of Aalborg and over 10 years of experience in the power industry, Lars possesses a deep understanding of grid operator requirements, grid connection procedures, and power system analysis. He has previously worked on several large-scale renewable energy projects, including wind farms and solar power plants, giving him direct experience with the challenges and opportunities of grid integration in Denmark. Lars's technical expertise, problem-solving skills, and ability to navigate complex grid regulations make him an invaluable asset to the solar farm project.
137
+
138
+ **Equipment Needs**:
139
+ Specialized software for power system studies (e.g., PSS/E, DigSilent), communication equipment for liaising with grid operators.
140
+
141
+ **Facility Needs**:
142
+ Office space, access to grid connection standards and regulations.
143
+
144
+ ---
145
+
146
+ ## Role 6 - Construction Supervisor
147
+
148
+ **Contract Type**: full_time_employee
149
+
150
+ **Contract Type Justification**: The Construction Supervisor needs to be on-site throughout the construction phase, ensuring quality and safety. A full-time employee provides consistent oversight.
151
+
152
+ **Explanation**:
153
+ Oversees the physical construction of the solar farm, ensuring adherence to engineering specifications, safety regulations, and quality standards.
154
+
155
+ **Consequences**:
156
+ Poor construction quality, safety hazards, and potential structural failures.
157
+
158
+ **People Count**:
159
+ 1
160
+
161
+ **Typical Activities**:
162
+ Overseeing the physical construction of the solar farm, ensuring adherence to engineering specifications, enforcing safety regulations, and maintaining quality standards.
163
+
164
+ **Background Story**:
165
+ Helle Mortensen, a highly experienced Construction Supervisor from Roskilde, has a proven track record of successfully overseeing the construction of large-scale infrastructure projects across Denmark. With a Bachelor's degree in Civil Engineering from the Technical University of Denmark and over 18 years of experience in the construction industry, Helle possesses a comprehensive understanding of construction methods, safety regulations, and quality control procedures. She has previously supervised the construction of several renewable energy projects, including wind farms and solar power plants, giving her direct experience with the challenges and opportunities of construction in the renewable energy sector. Helle's leadership skills, attention to detail, and commitment to safety make her the ideal candidate to oversee the construction of the solar farm.
166
+
167
+ **Equipment Needs**:
168
+ Personal Protective Equipment (PPE), communication devices (radio, phone), inspection tools, access to engineering drawings and specifications.
169
+
170
+ **Facility Needs**:
171
+ On-site office/trailer, access to construction site, safety equipment storage.
172
+
173
+ ---
174
+
175
+ ## Role 7 - Legal Counsel (Danish Law)
176
+
177
+ **Contract Type**: independent_contractor
178
+
179
+ **Contract Type Justification**: Legal Counsel is needed for specific legal tasks and advice. An independent contractor specializing in Danish law can provide this expertise as needed.
180
+
181
+ **Explanation**:
182
+ Provides legal expertise on Danish regulations related to renewable energy, land use, environmental compliance, and grid connection agreements.
183
+
184
+ **Consequences**:
185
+ Legal challenges, regulatory fines, and project delays due to non-compliance.
186
+
187
+ **People Count**:
188
+ min 1, max 2, depending on the complexity of the legal and regulatory landscape. This is measured by the number of unique legal challenges encountered during the project.
189
+
190
+ **Typical Activities**:
191
+ Providing legal expertise on Danish regulations related to renewable energy, land use, environmental compliance, and grid connection agreements, drafting and reviewing legal documents, and representing the project in legal proceedings.
192
+
193
+ **Background Story**:
194
+ Peter Olsen, a highly regarded Legal Counsel specializing in Danish law, operates his independent practice from Copenhagen. With a Juris Doctor degree from the University of Copenhagen and over 15 years of experience in legal practice, Peter possesses a deep understanding of Danish regulations related to renewable energy, land use, environmental compliance, and grid connection agreements. He has advised numerous clients on renewable energy projects, providing legal expertise on a wide range of issues, including permitting, financing, and contract negotiation. Peter's legal acumen, attention to detail, and commitment to client service make him the perfect choice to provide legal counsel on the solar farm project.
195
+
196
+ **Equipment Needs**:
197
+ Legal research databases, document drafting software, communication tools.
198
+
199
+ **Facility Needs**:
200
+ Office space, access to legal library/resources.
201
+
202
+ ---
203
+
204
+ ## Role 8 - Financial Analyst
205
+
206
+ **Contract Type**: full_time_employee
207
+
208
+ **Contract Type Justification**: The Financial Analyst needs to be involved throughout the project lifecycle, managing finances and securing funding. A full-time employee ensures consistent financial oversight.
209
+
210
+ **Explanation**:
211
+ Develops and manages the project's financial model, secures funding, and monitors financial performance throughout the project lifecycle.
212
+
213
+ **Consequences**:
214
+ Difficulty securing funding, cost overruns, and potential financial losses.
215
+
216
+ **People Count**:
217
+ min 1, max 2, depending on the size of the solar farm and the complexity of the financing structure. This is measured by the total capital expenditure of the project.
218
+
219
+ **Typical Activities**:
220
+ Developing and managing the project's financial model, securing funding, monitoring financial performance, and preparing financial reports.
221
+
222
+ **Background Story**:
223
+ Rasmus Christensen, a seasoned Financial Analyst from Odense, has a proven track record of successfully managing the financial aspects of large-scale infrastructure projects across Denmark. With a Master's degree in Finance from Copenhagen Business School and over 12 years of experience in the financial industry, Rasmus possesses a comprehensive understanding of financial modeling, project finance, and investment analysis. He has previously worked on several renewable energy projects, including wind farms and solar power plants, giving him direct experience with the challenges and opportunities of financing renewable energy projects in Denmark. Rasmus's analytical skills, financial expertise, and ability to secure funding make him an invaluable asset to the solar farm project.
224
+
225
+ **Equipment Needs**:
226
+ Financial modeling software, data analysis tools, communication tools for investor relations.
227
+
228
+ **Facility Needs**:
229
+ Office space, access to financial databases.