Sandra Sanchez commited on
Commit
ccc1b00
·
1 Parent(s): da080e4

Initial version with just scenarios to push into sxpaces

Browse files
.env.example ADDED
@@ -0,0 +1 @@
 
 
1
+ OPENAI_API_KEY='key'
.gitignore ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # System junk
10
+ .DS_Store
11
+
12
+ # Virtual environments
13
+ .venv
14
+ .env
15
+ venv/
16
+
17
+ # Generated images
18
+ generated_images/
19
+ images/
20
+ *.png
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.11
app/app.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ from pathlib import Path
4
+ from openai import OpenAI
5
+ import os
6
+ from dotenv import load_dotenv
7
+ import base64
8
+ import requests
9
+ import time
10
+
11
+
12
+ # Load environment variables from .env file
13
+ load_dotenv()
14
+
15
+ OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
16
+
17
+ client = OpenAI(api_key=OPENAI_API_KEY)
18
+ models = client.models.list()
19
+
20
+
21
+ TEMPLATES_DIR = Path(__file__).resolve().parent.parent / "mcp_server" / "templates"
22
+ GENERATED_IMAGES_DIR = Path(__file__).resolve().parent.parent / "generated_images"
23
+ GENERATED_IMAGES_DIR.mkdir(exist_ok=True)
24
+
25
+
26
+ def load_scenarios():
27
+ return [p.stem for p in TEMPLATES_DIR.glob("*.json")]
28
+
29
+
30
+ def generate_story(scenario_name: str):
31
+ filepath = TEMPLATES_DIR / f"{scenario_name}.json"
32
+ if not filepath.exists():
33
+ return "Template not found."
34
+ template = json.loads(filepath.read_text())
35
+ story_prompt = (
36
+ "Rewrite the following scenario into a simple, autism-friendly social story.\n"
37
+ "Use short sentences, gentle tone, clear structure, and supportive language.\n"
38
+ "Do NOT return JSON. Return only the story text.\n\n"
39
+ f"Scenario data:\n{json.dumps(template, indent=2)}"
40
+ )
41
+ story_resp = client.chat.completions.create(
42
+ model="gpt-4o-mini",
43
+ messages=[{"role": "user", "content": story_prompt}]
44
+ )
45
+ return story_resp.choices[0].message.content.strip()
46
+
47
+
48
+ def generate_image(scenario_name: str):
49
+ filepath = TEMPLATES_DIR / f"{scenario_name}.json"
50
+ if not filepath.exists():
51
+ return None
52
+ template = json.loads(filepath.read_text())
53
+ image_prompt = (
54
+ f"Create a soft, calming illustration for a social story about: {template['title']}."
55
+ " Use pastel colors, simple shapes, friendly characters, minimal details, no text."
56
+ )
57
+ # Comment out image generation while developing
58
+ # img = client.images.generate(
59
+ # model="gpt-image-1",
60
+ # prompt=image_prompt,
61
+ # output_format="png",
62
+ # size="512x512"
63
+ # )
64
+ # image_bytes = base64.b64decode(img.data[0].b64_json)
65
+ # timestamp = int(time.time())
66
+ # image_filename = f"{scenario_name}_{timestamp}.png"
67
+ # image_path = GENERATED_IMAGES_DIR / image_filename
68
+ # with open(image_path, "wb") as f:
69
+ # f.write(image_bytes)
70
+ # img_src = str(image_path)
71
+ # return img_src
72
+ # For now, return None or a public image URL
73
+ return None
74
+
75
+
76
+ # def main():
77
+ # print("Loading scenarios...")
78
+ # scenarios = load_scenarios()
79
+ # print("Scenarios loaded:", scenarios)
80
+ #
81
+ # with gr.Blocks() as demo:
82
+ # gr.Markdown("# 🧸 Comfortool\n### Autism-friendly social stories with calming illustrations")
83
+ #
84
+ # dropdown = gr.Dropdown(
85
+ # choices=scenarios,
86
+ # label="Choose a scenario"
87
+ # )
88
+ #
89
+ # generate_btn = gr.Button("Generate Social Story")
90
+ # story_out = gr.Textbox(label="Story", lines=12)
91
+ # image_out = gr.Image(label="Illustration")
92
+ #
93
+ # def on_generate(scenario_name):
94
+ # print("Generating story for:", scenario_name)
95
+ # story = generate_story(scenario_name)
96
+ # print("Story generated:", story)
97
+ # image = None # For now, do not generate image
98
+ # return story, image
99
+ #
100
+ # generate_btn.click(
101
+ # fn=on_generate,
102
+ # inputs=dropdown,
103
+ # outputs=[story_out, image_out]
104
+ # )
105
+ #
106
+ # print("Gradio app initialized.")
107
+ # return demo
108
+
109
+
110
+ def show_selected(scenario_name):
111
+ return f"You selected: {scenario_name}"
112
+
113
+ def main():
114
+ scenarios = load_scenarios()
115
+ with gr.Blocks() as demo:
116
+ gr.Markdown("# 🧸 Comfortool\n### Available scenarios")
117
+ dropdown = gr.Dropdown(choices=scenarios, label="Choose a scenario")
118
+ output = gr.Textbox(label="Selected scenario")
119
+ dropdown.change(fn=show_selected, inputs=dropdown, outputs=output)
120
+ return demo
121
+
122
+
123
+ if __name__ == "__main__":
124
+ demo = main()
125
+ demo.launch(server_name="0.0.0.0", server_port=7860)
126
+
127
+
mcp_server/server.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mcp.server.fastmcp import FastMCP
2
+ from pathlib import Path
3
+ import json
4
+
5
+ # Initialize MCP server
6
+ mcp = FastMCP("comfortool-mcp-server")
7
+
8
+ BASE_DIR = Path(__file__).resolve().parent
9
+ TEMPLATE_DIR = BASE_DIR / "templates"
10
+ IMAGES_DIR = BASE_DIR / "images"
11
+
12
+
13
+ # ---- Tool: Get list of scenarios ----
14
+ @mcp.tool()
15
+ def get_scenarios() -> list[str]:
16
+ """Return the list of available scenario template names."""
17
+ return [p.stem for p in TEMPLATE_DIR.glob("*.json")]
18
+
19
+
20
+ # ---- Tool: Get a specific scenario template ----
21
+ @mcp.tool()
22
+ def get_template(name: str) -> dict:
23
+ """Load and return a scenario JSON template by name."""
24
+ filepath = TEMPLATE_DIR / f"{name}.json"
25
+ if not filepath.exists():
26
+ raise FileNotFoundError(f"Template '{name}' not found.")
27
+ return json.loads(filepath.read_text())
28
+
29
+
30
+ # ---- Tool: List icons/images (optional for now) ----
31
+ @mcp.tool()
32
+ def get_static_images() -> list[str]:
33
+ """Return the filenames of static images/icons."""
34
+ return [p.name for p in IMAGES_DIR.glob("*")]
35
+
36
+
37
+ # ---- Run the server ----
38
+ if __name__ == "__main__":
39
+ mcp.run()
mcp_server/templates/dentist.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "going_to_the_dentist",
3
+ "title": "Going to the Dentist",
4
+ "story": "Today I am going to the dentist. The dentist helps keep my teeth clean and healthy. First, I will enter the quiet waiting room. Then, someone will call my name. I will sit in a big chair that goes up and down. The dentist will look at my teeth and might clean them. Some sounds may be loud, but they are safe. If I need a break, I can ask for one. When it is finished, I can go home feeling proud.",
5
+ "sensory": ["bright light", "whirring sounds"],
6
+ "tools": ["headphones", "fidget", "sunglasses"],
7
+ "emotion_support": "It is okay to feel nervous. Many people do."
8
+ }
mcp_server/templates/doctor.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "going_to_the_doctor",
3
+ "title": "Going to the Doctor",
4
+ "story": "Today I am visiting the doctor. The doctor keeps my body healthy. I will check in and wait until my name is called. The doctor may ask questions and gently check parts of my body. Sometimes, the doctor might use tools like a stethoscope. If I need a moment, I can say 'please wait.' After the visit, I can go home knowing I took good care of myself."
5
+ }
mcp_server/templates/first_day_school.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "first_day_of_school",
3
+ "title": "First Day of School",
4
+ "story": "Today is my first day of school. I will meet teachers and classmates. I will learn where my classroom is and where I can take breaks. There will be new routines, and that is okay. If things feel confusing, I can ask a teacher for help. Each day will feel more familiar."
5
+ }
mcp_server/templates/haircut.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "getting_a_haircut",
3
+ "title": "Getting a Haircut",
4
+ "story": "Today I am getting a haircut. The hairdresser helps my hair look nice. First, I will sit in a chair. Someone may touch my hair or use scissors. Scissors make small sounds, but they are safe. Hair may fall on my skin and feel tickly, but it can be brushed away. After the haircut, my head will feel lighter and fresh."
5
+ }
mcp_server/templates/loud_events.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "loud_and_crowded_events",
3
+ "title": "Loud and Crowded Places",
4
+ "story": "Today I might be in a loud or busy place. There may be many people and sounds. If it feels too loud, I can use headphones or take a break. I can stay close to someone I trust. I do not have to talk if I don’t want to. When the event is over, I can rest somewhere quiet."
5
+ }
mcp_server/templates/new_food.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "trying_a_new_food",
3
+ "title": "Trying a New Food",
4
+ "story": "Today I might try a new food. New food has a new smell, texture, or taste. I can look at the food first, smell it, touch it, or lick it. If I don’t like it, that is okay. Trying is already a success."
5
+ }
mcp_server/templates/new_place.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "visiting_a_new_place",
3
+ "title": "Visiting a New Place",
4
+ "story": "Today I will go somewhere I have not been before. New places can feel exciting or scary. I can look around slowly and learn what is there. I can stay close to someone I trust. When I’m ready, I can explore more. Every new place becomes familiar after a while."
5
+ }
mcp_server/templates/physical_education.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "physical_education_class",
3
+ "title": "PE Class",
4
+ "story": "In PE class, I will move my body and try fun activities. There may be loud sounds like balls bouncing or whistles. I can follow instructions at my own pace. If an activity feels too fast or confusing, I can ask for a pause. PE is for having fun and learning new skills."
5
+ }
mcp_server/templates/pickup_from_school.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "getting_picked_up",
3
+ "title": "Getting Picked Up From School",
4
+ "story": "When school is over, someone comes to pick me up. If I am playing, stopping can feel hard. I will get a reminder before it’s time to go. I can take one last turn or finish my activity. When I’m ready, I will leave calmly and go home."
5
+ }
mcp_server/templates/social_situations.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "id": "social_situations",
3
+ "title": "Social Situations",
4
+ "story": "Sometimes I will be around people and talk to them. I don’t have to talk a lot. I can use short words or gestures. If I need space, I can step away for a moment. Everyone communicates in different ways, and that is okay. Being myself is enough."
5
+ }
pyproject.toml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "comfortool"
3
+ version = "0.0.1"
4
+ requires-python = ">=3.10"
5
+ description = "Comfortool - autism-friendly social stories"
6
+ dependencies = [
7
+ "fastapi>=0.122.0",
8
+ "gradio>=6.0.1",
9
+ "openai>=2.8.1",
10
+ "pillow>=12.0.0",
11
+ "python-dotenv>=1.2.1",
12
+ "requests>=2.32.5",
13
+ "uvicorn>=0.38.0",
14
+ ]
15
+
16
+ [tool.uv]
17
+ # uv config can go here if needed (optional)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ openai
2
+ gradio
3
+ python-dotenv
4
+ requests
uv.lock ADDED
The diff for this file is too large to render. See raw diff