feat: add GUI

This commit is contained in:
Albert
2025-08-17 04:00:21 +00:00
parent b745151d2c
commit 06c108586c
7 changed files with 1459 additions and 11 deletions

View File

@@ -4,7 +4,8 @@
"Bash(mkdir:*)",
"Bash(python run_tests.py:*)",
"Bash(source:*)",
"Bash(pytest:*)"
"Bash(pytest:*)",
"Bash(python:*)"
],
"deny": [],
"ask": []

View File

@@ -2,6 +2,7 @@ import uuid
from datetime import datetime
from typing import List, Optional, Any
from sqlmodel import Field, SQLModel, Relationship, JSON, Column
from pydantic import BaseModel
class StartSessionRequest(SQLModel):
@@ -70,4 +71,30 @@ class Session(SQLModel, table=True):
transcript: List[dict] = Field(default=[], sa_column=Column(JSON))
created_at: datetime = Field(default_factory=datetime.utcnow)
notes: List["Note"] = Relationship(back_populates="session")
notes: List["Note"] = Relationship(back_populates="session")
# Response models for visualization
class NoteWithLinks(BaseModel):
id: uuid.UUID
title: str
content: str
tags: List[str]
created_at: datetime
links: List["LinkResponse"] = []
class LinkResponse(BaseModel):
id: int
source_note_id: uuid.UUID
target_note_id: uuid.UUID
relationship: str
confidence: float = 1.0
class SessionDataResponse(BaseModel):
session_id: uuid.UUID
status: str
notes: List[NoteWithLinks]
created_at: datetime
summary: str = ""

View File

@@ -95,4 +95,10 @@ class LinkRepository:
async def get_by_target(self, target_id: uuid.UUID) -> List[Link]:
statement = select(Link).where(Link.target_id == target_id)
result = await self.session.exec(statement)
return result.all()
async def get_by_session(self, session_id: uuid.UUID) -> List[Link]:
"""Get all links for notes in a session."""
statement = select(Link).join(Note, Link.source_id == Note.id).where(Note.session_id == session_id)
result = await self.session.exec(statement)
return result.all()

View File

@@ -5,6 +5,7 @@ from typing import Annotated
from fastapi import FastAPI, Depends, HTTPException, BackgroundTasks, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from app.core.config import settings
from app.data.database import init_db
@@ -12,7 +13,8 @@ from app.data.models import (
StartSessionRequest,
SendMessageRequest,
SessionResponse,
SessionStatusResponse
SessionStatusResponse,
SessionDataResponse
)
from app.services.session_service import SessionService
@@ -159,6 +161,37 @@ async def get_session_status(
raise HTTPException(status_code=500, detail="Failed to get session status")
@app.get("/sessions/getData", response_model=SessionDataResponse)
async def get_session_data(
session_id: uuid.UUID,
session_service: Annotated[SessionService, Depends(get_session_service)]
) -> SessionDataResponse:
"""Get session data with notes and links for visualization.
Args:
session_id: UUID of the session
session_service: Injected SessionService instance
Returns:
SessionDataResponse with notes and their links
"""
try:
result = await session_service.get_session_data(session_id)
return SessionDataResponse(
session_id=result["session_id"],
status=result["status"],
notes=result["notes"],
created_at=result["created_at"],
summary=result["summary"]
)
except ValueError as e:
logger.error(f"Session not found: {e}")
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error getting session data: {e}")
raise HTTPException(status_code=500, detail="Failed to get session data")
@app.get("/health")
async def health_check():
"""Health check endpoint."""
@@ -167,13 +200,14 @@ async def health_check():
@app.get("/")
async def root():
"""Root endpoint with API information."""
return {
"message": "Welcome to SkyTalk API",
"description": "AI-powered conversational idea exploration and knowledge synthesis",
"version": "1.0.0",
"docs": "/docs"
}
"""Serve the GUI interface at root."""
return FileResponse("gui.html")
@app.get("/session/{session_id}")
async def view_session(session_id: uuid.UUID):
"""Serve the GUI with a specific session loaded."""
return FileResponse("gui.html")
if __name__ == "__main__":

View File

@@ -202,6 +202,66 @@ class SessionService:
"created_at": session.created_at
}
async def get_session_data(self, session_id: uuid.UUID) -> Dict[str, any]:
"""Get session data with notes and links for visualization.
Args:
session_id: UUID of the session
Returns:
Dictionary containing session data with notes and links
"""
from app.data.models import NoteWithLinks, LinkResponse
async with get_session() as db:
session_repo = SessionRepository(db)
note_repo = NoteRepository(db)
link_repo = LinkRepository(db)
session = await session_repo.get(session_id)
if not session:
raise ValueError(f"Session {session_id} not found")
notes = await note_repo.get_by_session(session_id)
links = await link_repo.get_by_session(session_id)
# Create a map of note_id -> links for that note
links_by_source = {}
for link in links:
if link.source_id not in links_by_source:
links_by_source[link.source_id] = []
links_by_source[link.source_id].append(LinkResponse(
id=link.id,
source_note_id=link.source_id,
target_note_id=link.target_id,
relationship=link.context,
confidence=1.0 # Default confidence since we don't store it
))
# Create NoteWithLinks objects
notes_with_links = []
for note in notes:
note_links = links_by_source.get(note.id, [])
notes_with_links.append(NoteWithLinks(
id=note.id,
title=note.title,
content=note.content,
tags=note.tags,
created_at=note.created_at,
links=note_links
))
# Generate session summary
summary = await self._generate_session_summary(notes)
return {
"session_id": session.id,
"status": session.status,
"notes": notes_with_links,
"created_at": session.created_at,
"summary": summary
}
async def _create_links_for_notes(self, notes: List[Note], link_repo: LinkRepository) -> None:
"""Create semantic links for a list of new notes."""
for note in notes:
@@ -243,4 +303,30 @@ class SessionService:
logger.info(f"Created {len(links_to_create)} links for note {note.id}")
except Exception as e:
logger.error(f"Error creating links for note {note.id}: {e}")
logger.error(f"Error creating links for note {note.id}: {e}")
async def _generate_session_summary(self, notes: List[Note]) -> str:
"""Generate a high-level summary of the session's notes."""
if not notes:
return "No notes available for this session."
try:
# Create summary of all note titles and key themes
note_summaries = []
all_tags = set()
for note in notes:
note_summaries.append(f"{note.title}")
all_tags.update(note.tags)
summary_text = f"This session explored {len(notes)} key concepts:\n\n"
summary_text += "\n".join(note_summaries)
if all_tags:
summary_text += f"\n\nKey themes: {', '.join(sorted(all_tags))}"
return summary_text
except Exception as e:
logger.error(f"Error generating session summary: {e}")
return f"Session contains {len(notes)} synthesized notes on various topics."

1207
gui.html Normal file

File diff suppressed because it is too large Load Diff

87
tmp.txt Normal file
View File

@@ -0,0 +1,87 @@
You
copyright policy and its impact on society
AI
AI Interviewer
That's a very broad and important topic. To begin, what specific aspects of copyright policy's impact on society are you most interested in exploring?
U
You
let's talk about the ways it actually hurts artists
AI
AI Interviewer
That's an interesting perspective. Could you elaborate on some of the ways you believe copyright policy might actually harm artists, rather than protect them?
U
You
copyright is predicated on the idea that remedies will occur through the legal system rather than through social reward/punishment; as society currently stands, the former biases towards entities with access to better lawyers, which are almost always those who are already making a lot of money on their art because it's a nonlinear distribution of success. the latter would allow the art from the most popular artists to have spinoffs more easily (e.g. song remixes, legal public performances, clip shows, etc) that would benefit smaller performers
AI
AI Interviewer
That's a clear articulation of the challenge with the legal system's bias. Could you elaborate on what "social reward/punishment" would look like in practice, and how it would function to incentivize creators and regulate the use of their work without relying on legal remedies?
U
You
something like cc-by-sa -- attribution would be the legal requirement and the real stick would be the social cost that came with not giving someone credit. this is how, for instance, recipes work -- due to essentially the randomness of case law, recipes aren't protected by copyright, so recipe creators are shamed into not ripping off someone else's recipe entirely without credit, and it seems to function well there
You
I want to explore the topic: copyright policy and its impact on society
AI
AI Interviewer
That's a very broad and important topic. To start, what aspect of copyright policy's impact on society do you find most compelling or problematic, and why?
U
You
let's talk about the ways it actually hurts artists
AI
AI Interviewer
That's an interesting direction. To begin, could you describe one or two specific ways you believe current copyright policy harms artists?
U
You
copyright is predicated on the idea that remedies will occur through the legal system rather than through social reward/punishment; as society currently stands, the former biases towards entities with access to better lawyers, which are almost always those who are already making a lot of money on their art because it's a nonlinear distribution of success. the latter would allow the art from the most popular artists to have spinoffs more easily (e.g. song remixes, legal public performances, clip shows, etc) that would benefit smaller performers
AI
AI Interviewer
You've articulated a clear distinction between legal and social enforcement mechanisms. Could you elaborate on how the current legal system, by favoring those with access to better lawyers, specifically *hinders* the ability of less-resourced artists to protect or leverage their own creative work?
U
You
I mean that's definitionally true isn't it? if they have fewer resources to defend their art, they will not be able to defend it as well
AI
AI Interviewer
That's a fair point about the general principle. Could you give an example of a specific scenario where a less-resourced artist's inability to defend their work, due to legal costs or complexity, directly impacts their career or creative output?
U
You
hm let's talk about how attribution would have more benefits to artists
AI
AI Interviewer
That's an interesting pivot to attribution. Could you elaborate on how increased attribution, perhaps in a system emphasizing social reward, would specifically offer more benefits to artists than the current legal framework?
U
You
because it's more accessible to smaller artists and there are more eyes on the bigger ones so the social costs of ripping off small artists would be higher if we didn't have the legal punishment as a social crutch
AI
AI Interviewer
You're suggesting that the existence of legal punishment might actually *reduce* the social pressure to attribute or avoid infringement. Could you explain how the current legal framework acts as a "social crutch" in this context, effectively diminishing the social costs of appropriation?
U
You
because people view the "way to protect small artists" as "they should do it themselves and sue" so they're less attuned to ripoffs and therefore there aren't as many organized boycotts of ripoffs in music/film whereas that's more common in industries which, due to the randomness of case law, aren't protected by copyright like recipes or fashion
AI
AI Interviewer
You're suggesting that the perceived availability of legal recourse shifts the responsibility for enforcement away from the community and onto the individual artist. How does this shift in perceived responsibility specifically lead to the public being "less attuned to ripoffs" in copyright-protected fields like music and film?
U
You
because they don't view it as their responsibility to boycott/police
AI
AI Interviewer
So, if the public doesn't view it as their responsibility to boycott or police, what *does* influence their perception of responsibility in these areas?
U
You
the copyright system as it currently exists, therefore alleviating them of their social duty to hold artists to an attributive norm
AI
AI Interviewer
You're suggesting that the existence of the copyright system itself, by its very nature, absolves the public of this social duty. Could you elaborate on how the *design* or *operation* of the current copyright system specifically communicates this message to the public, leading them to feel less responsible for enforcing attributive norms?
U
You
well the copyright lobby has spent the last hundred years convincing society (through ad campaigns etc) that copyright is the solution and pirates are the problem, when it's stifling creativity from smaller artists because they're afraid of being sued even if they would eventually win in court because even a lawsuit would be too expensive so they don't do things like remix or clip or whatever
AI
AI Interviewer
You're suggesting that the message from the copyright lobby has framed the issue as "copyright is the solution and pirates are the problem." How does this specific framing, where "pirates" are the primary concern, lead the public to be less attuned to or less willing to act on instances where attribution is lacking, or where established artists might be "ripping off" smaller ones, rather than focusing on "piracy"?
U
You
okay let's wrap this up
AI
AI Interviewer
Thank you for this thoughtful conversation! I believe we've explored your ideas thoroughly.