File size: 4,331 Bytes
9b7c591
 
 
7f62453
 
3d60a43
7f62453
 
3d60a43
7f62453
 
 
 
 
 
 
 
 
 
 
 
3d60a43
7f62453
9b7c591
 
 
 
 
 
 
 
 
 
 
3d60a43
 
7f62453
 
3d60a43
9b7c591
3d60a43
9b7c591
 
3d60a43
9b7c591
 
7f62453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b7c591
7f62453
9b7c591
 
3d60a43
 
7f62453
3d60a43
 
7f62453
 
 
 
 
 
 
 
 
3d60a43
 
 
 
 
 
 
9b7c591
3d60a43
9b7c591
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
Crew definition for the agents service.

LLM is provided via LiteLLM against Hugging Face. Set the model id in the environment — see
AGENTS_LLM_MODEL in .env.example (LiteLLM form: huggingface/<org>/<model>).

Pick a model that your Hugging Face account can run via Inference Providers (check the model's Hub page
for Inference / provider badges), or set AGENTS_LLM_BASE_URL to your own OpenAI-compatible endpoint.

Qwen/WebWorld-32B is not served on the public Inference Providers router; use it only with
AGENTS_LLM_BASE_URL (e.g. your own HF Inference Endpoint or vLLM).

If a plain huggingface/<org>/<model> call fails, LiteLLM supports:
    huggingface/<provider>/<org>/<model>
only when that provider is listed on the model card for this model (do not guess the provider).

Required environment:
    HF_TOKEN (or HUGGINGFACE_HUB_TOKEN)
    AGENTS_LLM_MODEL

Optional:
    AGENTS_LLM_TEMPERATURE   float, default 0.5
    AGENTS_LLM_BASE_URL      OpenAI-compatible base URL (e.g. HF Inference Endpoint)
"""
from __future__ import annotations

import os
from typing import List

from crewai import Agent, Crew, LLM, Process, Task
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.project import CrewBase, agent, crew, task


def _resolve_hf_token() -> str:
    token = (os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN") or "").strip()
    if (token.startswith('"') and token.endswith('"')) or (token.startswith("'") and token.endswith("'")):
        token = token[1:-1].strip()
    if not token:
        raise RuntimeError(
            "Missing Hugging Face token. Set HF_TOKEN (or HUGGINGFACE_HUB_TOKEN) in the "
            "agents service environment."
        )
    return token


def _strip_optional_env_quotes(value: str) -> str:
    v = value.strip()
    if (v.startswith('"') and v.endswith('"')) or (v.startswith("'") and v.endswith("'")):
        v = v[1:-1].strip()
    return v


def _resolve_llm_model() -> str:
    raw = (os.getenv("AGENTS_LLM_MODEL") or "").strip()
    raw = _strip_optional_env_quotes(raw)
    if not raw:
        raise RuntimeError(
            "Missing AGENTS_LLM_MODEL. Set it in the environment (see .env.example), e.g. "
            "huggingface/Qwen/Qwen2.5-7B-Instruct"
        )
    return raw


def _build_llm() -> LLM:
    model = _resolve_llm_model()
    temperature = float(os.getenv("AGENTS_LLM_TEMPERATURE", "0.5"))

    hf_token = _resolve_hf_token()
    os.environ["HF_TOKEN"] = hf_token
    os.environ["HUGGINGFACE_HUB_TOKEN"] = hf_token

    base_url = (os.getenv("AGENTS_LLM_BASE_URL") or "").strip().rstrip("/")
    base_url = _strip_optional_env_quotes(base_url) if base_url else ""

    if "webworld" in model.lower() and not base_url:
        raise RuntimeError(
            "AGENTS_LLM_MODEL is set to a WebWorld model, which Hugging Face Inference Providers "
            "does not host. Set AGENTS_LLM_MODEL to a routed instruct model, or run WebWorld on your "
            "own endpoint and set AGENTS_LLM_BASE_URL to that OpenAI-compatible base URL."
        )

    if base_url:
        return LLM(
            model=model,
            base_url=base_url,
            api_key=hf_token,
            temperature=temperature,
        )

    return LLM(model=model, api_key=hf_token, temperature=temperature)


@CrewBase
class ContentCrew:
    """LinkedIn post writing crew — runs inside the agents service."""

    agents: List[BaseAgent]
    tasks: List[Task]

    @agent
    def writer_agent(self) -> Agent:
        return Agent(
            config=self.agents_config["writer_agent"],
            llm=_build_llm(),
            max_tokens=420,
            verbose=False,
        )

    @agent
    def editor_agent(self) -> Agent:
        return Agent(
            config=self.agents_config["editor_agent"],
            llm=_build_llm(),
            max_tokens=380,
            verbose=False,
        )

    @task
    def write_post_task(self) -> Task:
        return Task(config=self.tasks_config["write_post_task"])

    @task
    def edit_post_task(self) -> Task:
        return Task(config=self.tasks_config["edit_post_task"])

    @crew
    def crew(self) -> Crew:
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=False,
        )