Auto-update: Wed Jun 26 18:40:25 PDT 2024

This commit is contained in:
sanj 2024-06-26 18:40:25 -07:00
parent ab86abc2f2
commit 7ff36b96e4
6 changed files with 110 additions and 35 deletions

View file

@ -94,6 +94,32 @@ async def query_ollama(usr: str, sys: str = LLM_SYS_MSG, model: str = DEFAULT_LL
else: else:
L.DEBUG("No choices found in response") L.DEBUG("No choices found in response")
return None return None
async def query_ollama_multishot(
message_list: List[str],
sys: str = LLM_SYS_MSG,
model: str = DEFAULT_LLM,
max_tokens: int = 200
):
if len(message_list) % 2 == 0:
raise ValueError("message_list must contain an odd number of strings")
messages = [{"role": "system", "content": sys}]
for i in range(0, len(message_list), 2):
messages.append({"role": "user", "content": message_list[i]})
if i + 1 < len(message_list):
messages.append({"role": "assistant", "content": message_list[i+1]})
LLM = Ollama()
response = await LLM.chat(model=model, messages=messages, options={"num_predict": max_tokens})
L.DEBUG(response)
if "message" in response and "content" in response["message"]:
return response["message"]["content"]
else:
L.DEBUG("No content found in response")
return None
def is_vision_request(content): def is_vision_request(content):
return False return False

View file

@ -172,7 +172,7 @@ async def localize_datetime(dt, fetch_loc: bool = False):
def find_override_locations(lat: float, lon: float) -> Optional[str]: async def find_override_locations(lat: float, lon: float) -> Optional[str]:
# Load the JSON file # Load the JSON file
with open(NAMED_LOCATIONS, 'r') as file: with open(NAMED_LOCATIONS, 'r') as file:
locations = yaml.safe_load(file) locations = yaml.safe_load(file)
@ -510,21 +510,21 @@ async def get_last_location() -> Optional[Location]:
query_datetime = datetime.now(TZ) query_datetime = datetime.now(TZ)
L.DEBUG(f"Query_datetime: {query_datetime}") L.DEBUG(f"Query_datetime: {query_datetime}")
location = await fetch_last_location_before(query_datetime) this_location = await fetch_last_location_before(query_datetime)
if location: if this_location:
L.DEBUG(f"location: {location}") L.DEBUG(f"location: {this_location}")
return location return this_location
return None return None
@locate.get("/locate", response_model=Location) @locate.get("/locate", response_model=Location)
async def get_last_location_endpoint() -> JSONResponse: async def get_last_location_endpoint() -> JSONResponse:
location = await get_last_location() this_location = await get_last_location()
if location: if this_location:
location_dict = location.model_dump() location_dict = this_location.model_dump()
location_dict["datetime"] = location.datetime.isoformat() location_dict["datetime"] = this_location.datetime.isoformat()
return JSONResponse(content=location_dict) return JSONResponse(content=location_dict)
else: else:
raise HTTPException(status_code=404, detail="No location found before the specified datetime") raise HTTPException(status_code=404, detail="No location found before the specified datetime")

View file

@ -647,8 +647,29 @@ async def banner_endpoint(dt: str, location: str = None, mood: str = None, other
return jpg_path return jpg_path
async def get_note(date_time: datetime):
date_time = await locate.localize_datetime(date_time);
absolute_path, local_path = assemble_journal_path(date_time, filename = "Notes", extension = ".md", no_timestamp = True)
if absolute_path.is_file():
with open(absolute_path, 'r', encoding='utf-8') as file:
content = file.read()
return content if content else None
async def sentiment_analysis(date_time: datetime):
most_recent_note = await get_note(date_time)
most_recent_note = most_recent_note or await get_note(date_time - timedelta(days=1))
if most_recent_note:
sys_msg = "You are a sentiment analysis AI bot. Your task is to analyze text and give a one-word description of the mood it contains, such as 'optimistic', 'pensive', 'nostalgic', 'livid', et cetera."
prompt = f"Provide sentiment analysis of the following notes: {most_recent_note}"
multishot_prompt = ["Provide sentiment analysis of the following notes: I am sad today my girlfriend broke up with me", "lonely", "Provide sentiment analysis of the following notes: Work has been so busy lately it is like there are not enough hours in the day", "hectic", prompt]
analysis = await llm.query_ollama_multishot(multishot_prompt, sys_msg, max_tokens = 10)
return analysis
else:
return ""
async def generate_banner(dt, location: Location = None, forecast: str = None, mood: str = None, other_context: str = None): async def generate_banner(dt, location: Location = None, forecast: str = None, mood: str = None, other_context: str = None):
L.DEBUG(f"Location: {location}, forecast: {forecast}, mood: {mood}, other_context: {other_context}") # L.DEBUG(f"Location: {location}, forecast: {forecast}, mood: {mood}, other_context: {other_context}")
date_time = await locate.localize_datetime(dt) date_time = await locate.localize_datetime(dt)
L.DEBUG(f"generate_banner called with date_time: {date_time}") L.DEBUG(f"generate_banner called with date_time: {date_time}")
destination_path, local_path = assemble_journal_path(date_time, filename="Banner", extension=".jpg", no_timestamp = True) destination_path, local_path = assemble_journal_path(date_time, filename="Banner", extension=".jpg", no_timestamp = True)
@ -662,12 +683,12 @@ async def generate_banner(dt, location: Location = None, forecast: str = None, m
display_name = "Location: " display_name = "Location: "
if location: if location:
lat, lon = location.latitude, location.longitude lat, lon = location.latitude, location.longitude
override_location = locate.find_override_locations(lat, lon) override_location = await locate.find_override_locations(lat, lon)
display_name += f"{override_location}, " if override_location else "" display_name += f"{override_location}, " if override_location else ""
if location.display_name: if location.display_name:
display_name += f"{location.display_name}" display_name += f"{location.display_name}"
elif: else:
display_name += f"{location.road}, " if location.road else "" display_name += f"{location.road}, " if location.road else ""
display_name += f"the {location.neighbourhood} neighbourhood of " if location.neighbourhood else "" display_name += f"the {location.neighbourhood} neighbourhood of " if location.neighbourhood else ""
display_name += f"the {location.suburb} suburb of " if location.suburb else "" display_name += f"the {location.suburb} suburb of " if location.suburb else ""
@ -676,18 +697,43 @@ async def generate_banner(dt, location: Location = None, forecast: str = None, m
display_name += f"{location.state} " if location.state else "" display_name += f"{location.state} " if location.state else ""
display_name += f"{location.country} " if location.country else "" display_name += f"{location.country} " if location.country else ""
if display_name == "Location: ":
geocoded_location = await locate.reverse_geocode(lat, lon)
if geocoded_location.display_name or geocoded_location.city or geocoded_location.country:
return await generate_banner(dt, geocoded_location, forecast, mood, other_context)
else:
L.WARN(f"Failed to get a useable location for purposes of generating a banner, but we'll generate one anyway.")
if not forecast: if not forecast:
forecast = "The weather forecast is: " + await update_dn_weather(date_time) forecast = "The weather forecast is: " + await update_dn_weather(date_time)
sentiment = await sentiment_analysis(date_time)
mood = sentiment if not mood else mood
mood = f"Mood: {mood}" if mood else "" mood = f"Mood: {mood}" if mood else ""
other_context = f"Additional information: {other_context}" if other_context else ""
if mood and sentiment: mood = f"Mood: {mood}, {sentiment}"
elif mood and not sentiment: mood = f"Mood: {mood}"
elif sentiment and not mood: mood = f"Mood: {sentiment}"
else: mood = ""
events = await calendar.get_events(date_time, date_time)
formatted_events = []
for event in events:
event_str = event.get('name')
if event.get('location'):
event_str += f" at {event.get('location')}"
formatted_events.append(event_str)
additional_info = ', '.join(formatted_events) if formatted_events else ''
other_context = f"{other_context}, {additional_info}" if other_context else additional_info
other_context = f"Additional information: {other_context}" if other_context else ""
prompt = "Generate an aesthetically appealing banner image for a daily note that helps to visualize the following scene information: " prompt = "Generate an aesthetically appealing banner image for a daily note that helps to visualize the following scene information: "
prompt += "\n".join([display_name, forecast, mood, other_context]) prompt += "\n".join([display_name, forecast, mood, other_context])
L.DEBUG(f"Prompt: {prompt}") L.DEBUG(f"Prompt: {prompt}")
# sd.workflow(prompt: str, scene: str = None, size: str = None, style: str = "photorealistic", earlyurl: bool = False, destination_path: str = None): # sd.workflow(prompt: str, scene: str = None, size: str = None, style: str = "photorealistic", earlyurl: bool = False, destination_path: str = None):
final_path = await sd.workflow(prompt, scene=OBSIDIAN_BANNER_SCENE, size="1080x512", style="romantic", earlyout="local", destination_path=destination_path) final_path = await sd.workflow(prompt, scene=OBSIDIAN_BANNER_SCENE, size="1080x512", style="romantic", destination_path=destination_path)
if not str(local_path) in str(final_path): if not str(local_path) in str(final_path):
L.INFO(f"Apparent mismatch between local path, {local_path}, and final_path, {final_path}") L.INFO(f"Apparent mismatch between local path, {local_path}, and final_path, {final_path}")
@ -705,7 +751,7 @@ async def note_weather_get(
): ):
try: try:
date_time = datetime.now() if date == "0" else locate.localize_datetime(date) date_time = datetime.now() if date == "0" else await locate.localize_datetime(date)
L.DEBUG(f"date: {date} .. date_time: {date_time}") L.DEBUG(f"date: {date} .. date_time: {date_time}")
content = await update_dn_weather(date_time) #, lat, lon) content = await update_dn_weather(date_time) #, lat, lon)
return JSONResponse(content={"forecast": content}, status_code=200) return JSONResponse(content={"forecast": content}, status_code=200)
@ -735,7 +781,7 @@ async def update_dn_weather(date_time: datetime):
lat = place.latitude lat = place.latitude
lon = place.longitude lon = place.longitude
city = locate.find_override_locations(lat, lon) city = await locate.find_override_locations(lat, lon)
if city: if city:
L.INFO(f"Using override location: {city}") L.INFO(f"Using override location: {city}")
@ -745,7 +791,7 @@ async def update_dn_weather(date_time: datetime):
L.INFO(f"City in data: {city}") L.INFO(f"City in data: {city}")
else: else:
loc = locate.reverse_geocode(lat, lon) loc = await locate.reverse_geocode(lat, lon)
L.DEBUG(f"loc: {loc}") L.DEBUG(f"loc: {loc}")
city = loc.name city = loc.name
city = city if city else loc.city city = city if city else loc.city
@ -801,7 +847,8 @@ async def update_dn_weather(date_time: datetime):
detailed_forecast = ( detailed_forecast = (
f"---\n" f"---\n"
f"date: {date_str}\n" f"date: {date_str}\n"
f"zip: {zip}\n" f"latitude: {lat}"
f"longitude: {lon}"
f"tags:\n" f"tags:\n"
f" - weather\n" f" - weather\n"
f"updated: {now}\n" f"updated: {now}\n"

View file

@ -59,17 +59,18 @@ async def sd_endpoint(request: Request):
@sd.get("/v1/images/generations") @sd.get("/v1/images/generations")
async def sd_endpoint( async def sd_endpoint(
request: Request, request: Request,
prompt: str = Query(..., description="The prompt for image generation") prompt: str = Query(..., description="The prompt for image generation"),
earlyout: str = Query("output", description="specify web for a redirect, or json for a json with the local path")
): ):
image_path = await workflow(prompt=prompt, scene="wallpaper", earlyout=earlyout)
web_path = get_web_path(image_path)
earlyout = "web"
image_path = await workflow(prompt=prompt, scene="wallpaper", earlyout="web")
if earlyout == "web": if earlyout == "web":
return RedirectResponse(url=image_path, status_code=303) return RedirectResponse(url=web_path, status_code=303)
else: else:
return JSONResponse({"image_url": image_path}) return JSONResponse({"image_url": image_path})
async def workflow(prompt: str, scene: str = None, size: str = None, style: str = "photorealistic", earlyout: str = "local", destination_path: str = None, downscale_to_fit: bool = False): async def workflow(prompt: str, scene: str = None, size: str = None, style: str = "photorealistic", earlyout: str = None, destination_path: str = None, downscale_to_fit: bool = False):
scene_data = get_scene(scene) scene_data = get_scene(scene)
if not scene_data: if not scene_data:
scene_data = get_matching_scene(prompt) scene_data = get_matching_scene(prompt)
@ -105,14 +106,15 @@ async def workflow(prompt: str, scene: str = None, size: str = None, style: str
max_size = max(width, height) if downscale_to_fit else None max_size = max(width, height) if downscale_to_fit else None
destination_path = Path(destination_path).with_suffix(".jpg") if destination_path else SD_IMAGE_DIR / f"{prompt_id}.jpg" destination_path = Path(destination_path).with_suffix(".jpg") if destination_path else SD_IMAGE_DIR / f"{prompt_id}.jpg"
if not earlyout: if earlyout:
await generate_and_save_image(prompt_id, saved_file_key, max_size, destination_path) asyncio.create_task(generate_and_save_image(prompt_id, saved_file_key, max_size, destination_path))
L.DEBUG(f"Returning {destination_path}")
return destination_path
else: else:
asyncio.create_task(generate_and_save_image(prompt_id, saved_file_key, max_size, destination_path)) await generate_and_save_image(prompt_id, saved_file_key, max_size, destination_path)
L.DEBUG(f"Returning {destination_path}")
await asyncio.sleep(0.5) return destination_path
return get_web_path(destination_path) if earlyout == "web" else destination_path
async def generate_and_save_image(prompt_id, saved_file_key, max_size, destination_path): async def generate_and_save_image(prompt_id, saved_file_key, max_size, destination_path):

View file

@ -33,7 +33,7 @@ from sijapi import (
MAC_UN, MAC_PW, MAC_ID, TS_TAILNET, DATA_DIR, SD_IMAGE_DIR, PUBLIC_KEY, OBSIDIAN_VAULT_DIR MAC_UN, MAC_PW, MAC_ID, TS_TAILNET, DATA_DIR, SD_IMAGE_DIR, PUBLIC_KEY, OBSIDIAN_VAULT_DIR
) )
from sijapi.utilities import bool_convert, sanitize_filename, assemble_journal_path from sijapi.utilities import bool_convert, sanitize_filename, assemble_journal_path
from sijapi.routers.locate import localize_datetime from sijapi.routers import note, locate
serve = APIRouter(tags=["public"]) serve = APIRouter(tags=["public"])
@ -67,9 +67,9 @@ def is_valid_date(date_str: str) -> bool:
return False return False
@serve.get("/notes/{file_path:path}") @serve.get("/notes/{file_path:path}")
async def get_file(file_path: str): async def get_file_endpoint(file_path: str):
try: try:
date_time = await localize_datetime(file_path); date_time = await locate.localize_datetime(file_path);
absolute_path, local_path = assemble_journal_path(date_time, no_timestamp = True) absolute_path, local_path = assemble_journal_path(date_time, no_timestamp = True)
except ValueError as e: except ValueError as e:
L.DEBUG(f"Unable to parse {file_path} as a date, now trying to use it as a local path") L.DEBUG(f"Unable to parse {file_path} as a date, now trying to use it as a local path")

View file

@ -99,7 +99,7 @@ async def store_weather_to_db(date_time: datetime, weather_data: dict):
# Get location details from weather data if available # Get location details from weather data if available
longitude = weather_data.get('longitude') longitude = weather_data.get('longitude')
latitude = weather_data.get('latitude') latitude = weather_data.get('latitude')
elevation = locate.get_elevation(latitude, longitude) # 152.4 # default until we add a geocoder that can look up actual elevation; weather_data.get('elevation') # assuming 'elevation' key, replace if different elevation = await locate.get_elevation(latitude, longitude) # 152.4 # default until we add a geocoder that can look up actual elevation; weather_data.get('elevation') # assuming 'elevation' key, replace if different
location_point = f"POINTZ({longitude} {latitude} {elevation})" if longitude and latitude and elevation else None location_point = f"POINTZ({longitude} {latitude} {elevation})" if longitude and latitude and elevation else None
# Correct for the datetime objects # Correct for the datetime objects