Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions LLM.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,52 @@
import os
import requests
import json
import socket
import logging

#TODO: Dont hard code these, need to see how sugar as a whole manages API Keys
from sugar3.activity.activity import get_activity_root

API_URL = "https://ai.sugarlabs.org/ask-llm-prompted"
try:
with open("API_KEY.txt", "r") as f:
API_KEY = f.read().strip()
except OSError:
logging.error("Missing API_KEY.txt file.")
API_KEY = None

# Store and read the API key from the activity's persistent data directory
# instead of a flat file in the bundle. The data/ subdirectory of
# get_activity_root() survives across activity invocations and is the
# Sugar-standard location for per-activity configuration.
_API_KEY_FILENAME = "api_key"


def _get_api_key_path():
"""Return the path to the API key file inside activity_root/data/."""
data_dir = os.path.join(get_activity_root(), "data")
os.makedirs(data_dir, exist_ok=True)
return os.path.join(data_dir, _API_KEY_FILENAME)


def _read_api_key():
"""Read the API key from the activity data directory."""
path = _get_api_key_path()
try:
with open(path, "r") as f:
key = f.read().strip()
if key:
return key
except OSError:
pass
logging.error(
"Missing API key. Save your key to: %s", path
)
return None


def save_api_key(key):
"""Persist *key* so it is available on next launch."""
path = _get_api_key_path()
with open(path, "w") as f:
f.write(key.strip())
logging.info("API key saved to %s", path)


API_KEY = _read_api_key()

DEFAULT_PROMPT = "You are a friendly teacher named Jane who is 28 years old. You teach 10 year old children. Always give helpful, educational responses in simple words that children can understand. Keep your answers between 20-40 words. Be encouraging and enthusiastic but never use emojis(ever). If you notice spelling mistakes, gently correct them. Stay focused on the topic and give relevant answers."

Expand All @@ -25,7 +61,7 @@ def is_connected():

def ask_llm_prompted(question, custom_prompt = DEFAULT_PROMPT, timeout=120, max_length=200):
if API_KEY is None:
logging.error("Missing API key file: API_KEY.txt")
logging.error("Missing API key. Use save_api_key() or place it in activity_root/data/api_key")
Comment thread
adarshkumar23 marked this conversation as resolved.
Outdated
return False

if not is_connected():
Expand Down Expand Up @@ -88,4 +124,4 @@ def ask_llm_prompted(question, custom_prompt = DEFAULT_PROMPT, timeout=120, max_
print(f'LLM ANS: {answer}')

else:
print("Error, LLM did not respond")
print("Error, LLM did not respond")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No newline in EOF. Diff your changes before pushing from next time, you'll be able to catch these.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 57cb868. Here's the diff confirming the change:

Screenshot 2026-03-27 004539

Also noted — will run git diff --staged before every commit going forward.

Screenshot 2026-03-27 004556
Fixing.EOF.Line.in.LLM.py.and.Review.Comments.mp4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was asking about a screen recording of you running these changes inside the activity and not just running LLM.py in your terminal.
Please send that video. Be sure to test it inside of sugar.

19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,22 @@ The `not-gstreamer1` branch is a backport of features and bug fixes
from the `master` branch for ongoing maintenance of the activity on
Fedora 18 systems which don't have well-functioning GStreamer 1
packages.

API Key Configuration
=====================

Speak-AI uses the [Sugar-AI](https://ai.sugarlabs.org/) backend for
LLM-powered chatbot responses. An API key is required for this feature.

The key is stored in the activity's persistent data directory
(`<activity_root>/data/api_key`), which is the standard Sugar location
for per-activity configuration that survives across sessions.

To set up your API key, create the file manually:
Comment thread
adarshkumar23 marked this conversation as resolved.
Outdated
```
echo "YOUR_API_KEY" > ~/.sugar/default/vu.lux.olpc.Speak/data/api_key
```

The exact path may vary depending on your Sugar profile. If the key is
missing, the activity will log the expected path on startup and fall
back to the on-device SLM or AIML brain.
2 changes: 1 addition & 1 deletion activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
except ImportError:
USING_BRAIN = True

from LLM import is_connected, ask_llm_prompted, DEFAULT_PROMPT
from LLM import is_connected, ask_llm_prompted, DEFAULT_PROMPT, save_api_key
from GenAI import is_profane

SERVICE = 'org.sugarlabs.Speak'
Expand Down