Auto-update: Wed Jun 26 18:40:25 PDT 2024
This commit is contained in:
parent
ab86abc2f2
commit
7ff36b96e4
6 changed files with 110 additions and 35 deletions
|
@ -95,6 +95,32 @@ async def query_ollama(usr: str, sys: str = LLM_SYS_MSG, model: str = DEFAULT_LL
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
||||||
|
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 ""
|
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"
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue