File size: 2,967 Bytes
325b94c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# app/core/content_service.py
from concurrent.futures import ThreadPoolExecutor
from crewai import Crew, Process
from .content_crew_config import safe_run_cached
from agents.content_phase import (
    course_writer_agent,
    course_writer_task,
)  # ุงู†ุช ุนู†ุฏูƒ ุฏูˆู„ ุฌุงู‡ุฒูŠู†

MAX_UNIT_WORKERS = 3


def process_unit(unit: dict, course_title: str) -> dict:
    output_unit = {
        "unit_name": unit["unit_name"],
        "unit_outcome": unit["outcome"],
        "topics": [],
    }

    # ====== CACHED CREW (ูŠูุณุชุฎุฏู… ููŠ ุฃูŠ ู…ูƒุงู†) ======
    agent = course_writer_agent()
    task = course_writer_task(agent)
    cached_crew = Crew(
        agents=[agent],
        tasks=[task],
        process=Process.sequential,
        memory=False,
        verbose=False,
    )

    for topic in unit["topics"]:
        topic_entry = {"topic_title": topic["title"], "subtopics": []}

        # sequential subtopics for highest quality
        for sub in topic["subtopics"]:
            combined_scraped = "\n\n".join(
                r.get("scraped_content", "")[:1500]
                for r in sub.get("results", [])
                if "scraped_content" in r
            )

            inputs = {
                "course_title": course_title,
                "unit_title": unit["unit_name"],
                "topic_title": topic["title"],
                "subtopic_title": sub["title"],
                "subtopic_description": sub["description"],
                "combined_scraped_texts": combined_scraped,
            }

            print(f"๐ŸŸข Writing: {sub['title']}")


            generated, used_sources = safe_run_cached(inputs, cached_crew=cached_crew)

            topic_entry["subtopics"].append(
                {
                    "title": sub["title"],
                    "description": sub["description"],
                    "generated_content": generated,
                    "sources": used_sources,
                }
            )

        output_unit["topics"].append(topic_entry)

    return output_unit


def generate_course_content(course_data: dict) -> dict:
    """
    ุฏูŠ ุงู„ู„ูŠ ู‡ุชู†ุงุฏูŠู‡ุง ู…ู† ุงู„ู€ FastAPI
    course_data ุดูƒู„ู‡ุง ุฒูŠ ุงู„ู„ูŠ ูƒุงู† ููŠ ุงู„ูุงูŠู„:
      {
        "course_name": ...,
        "course_audience": ...,
        "units": [...]
      }
    """
    units = course_data["units"]
    final_results_ordered = [None] * len(units)

    futures = []
    with ThreadPoolExecutor(max_workers=MAX_UNIT_WORKERS) as executor:
        for idx, unit in enumerate(units):
            future = executor.submit(process_unit, unit, course_data["course_name"])
            futures.append((idx, future))

        for idx, future in futures:
            final_results_ordered[idx] = future.result()

    output = {
        "course_name": course_data["course_name"],
        "course_audience": course_data["course_audience"],
        "results": final_results_ordered,
    }

    return output