File size: 4,424 Bytes
488c973
2c01af9
 
 
 
 
 
 
 
488c973
 
 
 
 
 
 
 
2c01af9
 
 
488c973
 
 
 
 
 
2c01af9
 
 
488c973
2c01af9
488c973
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c01af9
 
 
488c973
 
 
 
 
 
c40b04e
488c973
 
 
 
 
 
 
 
 
 
2c01af9
df7fe41
c40b04e
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
import os, json, tempfile, pathlib, zipfile, io, spaces, time
spaces.GPU(lambda: None)()

import gradio as gr
from huggingface_hub import InferenceClient

MODEL = os.environ.get("SKILLS_MODEL", "meta-llama/Llama-3.1-8B-Instruct")
TOKEN = os.environ.get("OPENAI_API_KEY", "")

SYS = (
    "You are SkillBot, helping users create Codex skills. Follow this process:\n"
    "1. Understand: Ask what the skill does, get 2-3 concrete scenarios\n"
    "2. Name: Suggest a hyphen-case name and confirm\n"
    "3. Generate: When ready, output ONLY a JSON object on one line with these fields:\n"
    '   {"skill_name":"...","display_name":"...","description":"...","short_description":"...","default_prompt":"...","skill_body":"markdown body"}\n'
    "Rules: Ask 1-2 questions at a time. For skill_body write real markdown with Overview, Quick Start, Workflow. Speak Chinese if user speaks Chinese."
)

def chat(message, history):
    msgs = [{"role": "system", "content": SYS}]
    for m in (history or []):
        if isinstance(m, dict):
            msgs.append(m)
        elif isinstance(m, (list, tuple)):
            msgs.append({"role": "user", "content": m[0]})
            if m[1]: msgs.append({"role": "assistant", "content": m[1]})
    msgs.append({"role": "user", "content": message})

    client = InferenceClient(model=MODEL, token=TOKEN)
    resp = client.chat_completion(messages=msgs, max_tokens=2048, temperature=0.7)
    reply = resp.choices[0].message.content

    # Check if model output contains skill JSON
    skill_data = None
    import re
    for match in re.finditer(r"\{", reply):
        start = match.start()
        depth, end = 0, len(reply)
        for i in range(start, len(reply)):
            if reply[i] == "{": depth += 1
            elif reply[i] == "}":
                depth -= 1
                if depth == 0: end = i + 1; break
        candidate = reply[start:end]
        try:
            data = json.loads(candidate)
            if "skill_name" in data and "skill_body" in data:
                skill_data = data
                reply = reply[:start].strip() + "\n\nSkill generated! Click download below."
                break
        except: pass

    # Save skill if generated
    zip_path = None
    if skill_data:
        sd = skill_data
        # Build skill files in temp dir
        tmp = tempfile.mkdtemp()
        skill_dir = pathlib.Path(tmp) / sd["skill_name"]
        skill_dir.mkdir()
        agents = skill_dir / "agents"; agents.mkdir()
        md = f'---\nname: {sd["skill_name"]}\ndescription: "{sd.get("description","")}"\n---\n{sd["skill_body"]}'
        (skill_dir / "SKILL.md").write_text(md, encoding="utf-8")
        yaml = f'display_name: "{sd.get("display_name",sd["skill_name"])}"\nshort_description: "{sd.get("short_description","")}"\ndefault_prompt: "{sd.get("default_prompt","")}"'
        (agents / "openai.yaml").write_text(yaml, encoding="utf-8")
        # Create zip
        buf = io.BytesIO()
        with zipfile.ZipFile(buf, "w") as zf:
            zf.writestr(f'{sd["skill_name"]}/SKILL.md', md)
            zf.writestr(f'{sd["skill_name"]}/agents/openai.yaml', yaml)
        zip_path = f"/tmp/{sd['skill_name']}.zip"
        with open(zip_path, "wb") as f: f.write(buf.getvalue())

    history.append({"role": "user", "content": message})
    history.append({"role": "assistant", "content": reply})

    if zip_path:
        return history, gr.File(value=zip_path, visible=True)
    return history, gr.File(visible=False)

with gr.Blocks(title="SkillBot", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# SkillBot - Create a Codex Skill by Chatting")
    chatbot = gr.Chatbot(label="Conversation", height=400)
    msg = gr.Textbox(placeholder="Describe what skill you want to create...", label="Message")
    with gr.Row():
        send = gr.Button("Send", variant="primary")
        clear_btn = gr.Button("New Skill")
    file_out = gr.File(label="Download Skill", visible=False)

    send.click(chat, [msg, chatbot], [chatbot, file_out]).then(lambda: "", None, [msg])
    msg.submit(chat, [msg, chatbot], [chatbot, file_out]).then(lambda: "", None, [msg])
    clear_btn.click(lambda: ([], gr.File(visible=False)), None, [chatbot, file_out])

demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))

# v1785312598.4308667