feat: creating events and attaching locations

This commit is contained in:
2025-03-26 16:16:48 +00:00
parent caf168c7a1
commit f90876f499
8 changed files with 185 additions and 62 deletions

View File

@ -0,0 +1,18 @@
//
// Code generated by go-jet DO NOT EDIT.
//
// WARNING: Changes to this file may cause incorrect behavior
// and will be lost if the code is regenerated
//
package model
import (
"github.com/google/uuid"
)
type UserEvents struct {
ID uuid.UUID `sql:"primary_key"`
EventID uuid.UUID
UserID uuid.UUID
}

View File

@ -18,6 +18,7 @@ func UseSchema(schema string) {
ImageTags = ImageTags.FromSchema(schema)
ImageText = ImageText.FromSchema(schema)
Locations = Locations.FromSchema(schema)
UserEvents = UserEvents.FromSchema(schema)
UserImages = UserImages.FromSchema(schema)
UserImagesToProcess = UserImagesToProcess.FromSchema(schema)
UserLocations = UserLocations.FromSchema(schema)

View File

@ -0,0 +1,81 @@
//
// Code generated by go-jet DO NOT EDIT.
//
// WARNING: Changes to this file may cause incorrect behavior
// and will be lost if the code is regenerated
//
package table
import (
"github.com/go-jet/jet/v2/postgres"
)
var UserEvents = newUserEventsTable("haystack", "user_events", "")
type userEventsTable struct {
postgres.Table
// Columns
ID postgres.ColumnString
EventID postgres.ColumnString
UserID postgres.ColumnString
AllColumns postgres.ColumnList
MutableColumns postgres.ColumnList
}
type UserEventsTable struct {
userEventsTable
EXCLUDED userEventsTable
}
// AS creates new UserEventsTable with assigned alias
func (a UserEventsTable) AS(alias string) *UserEventsTable {
return newUserEventsTable(a.SchemaName(), a.TableName(), alias)
}
// Schema creates new UserEventsTable with assigned schema name
func (a UserEventsTable) FromSchema(schemaName string) *UserEventsTable {
return newUserEventsTable(schemaName, a.TableName(), a.Alias())
}
// WithPrefix creates new UserEventsTable with assigned table prefix
func (a UserEventsTable) WithPrefix(prefix string) *UserEventsTable {
return newUserEventsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
}
// WithSuffix creates new UserEventsTable with assigned table suffix
func (a UserEventsTable) WithSuffix(suffix string) *UserEventsTable {
return newUserEventsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
}
func newUserEventsTable(schemaName, tableName, alias string) *UserEventsTable {
return &UserEventsTable{
userEventsTable: newUserEventsTableImpl(schemaName, tableName, alias),
EXCLUDED: newUserEventsTableImpl("", "excluded", ""),
}
}
func newUserEventsTableImpl(schemaName, tableName, alias string) userEventsTable {
var (
IDColumn = postgres.StringColumn("id")
EventIDColumn = postgres.StringColumn("event_id")
UserIDColumn = postgres.StringColumn("user_id")
allColumns = postgres.ColumnList{IDColumn, EventIDColumn, UserIDColumn}
mutableColumns = postgres.ColumnList{EventIDColumn, UserIDColumn}
)
return userEventsTable{
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns
ID: IDColumn,
EventID: EventIDColumn,
UserID: UserIDColumn,
AllColumns: allColumns,
MutableColumns: mutableColumns,
}
}

View File

@ -23,9 +23,12 @@ It is possible that there is no location or event on an image.
You should ask for a list of locations, as the user is likely to have this location saved. Reuse existing locations where possible.
Always reuse existing locations from listLocations. Do not create duplicates.
Do not create an event if you don't see any dates, or a name indicating an event.
Always reuse existing locations from listLocations . Do not create duplicates.
Events can have an associated location, if you think there is a location, then you must either use a location from listLocations or you must create it first.
Wherever possible, find the location in the image.
`
// TODO: this should be read directly from a file on load.
@ -64,23 +67,6 @@ const TOOLS = `
}
}
},
{
"type": "function",
"function": {
"name": "attachImageLocation",
"description": "Add a location to an image. You must use UUID.",
"parameters": {
"type": "object",
"properties": {
"locationId": {
"type": "string",
"description": "UUID of an existing location, you can use listLocations to get values, or use the return value of createLocation"
}
},
"required": ["locationId"]
}
}
},
{
"type": "function",
"function": {
@ -139,6 +125,12 @@ type AttachImageLocationArguments struct {
LocationId string `json:"locationId"`
}
type CreateEventArguments struct {
Name string `json:"name"`
Datetime string `json:"datetime"`
LocationId string `json:"locationId"`
}
func (agent EventLocationAgent) GetLocations(userId uuid.UUID, imageId uuid.UUID, imageName string, imageData []byte) error {
var tools any
err := json.Unmarshal([]byte(TOOLS), &tools)
@ -299,6 +291,8 @@ func NewLocationEventAgent(locationModel models.LocationModel, eventModel models
},
}
// I'm not sure this one actually makes sense either.
// I think the earlier tool can do more.
toolHandler.Handlers["attachImageLocation"] = ToolHandler[AttachImageLocationArguments, model.ImageLocations]{
FunctionName: "attachImageLocation",
Parse: func(stringArgs string) (AttachImageLocationArguments, error) {
@ -312,6 +306,34 @@ func NewLocationEventAgent(locationModel models.LocationModel, eventModel models
},
}
toolHandler.Handlers["createEvent"] = ToolHandler[CreateEventArguments, model.Events]{
FunctionName: "createEvent",
Parse: func(stringArgs string) (CreateEventArguments, error) {
args := CreateEventArguments{}
err := json.Unmarshal([]byte(stringArgs), &args)
return args, err
},
Fn: func(info ToolHandlerInfo, args CreateEventArguments, call ToolCall) (model.Events, error) {
ctx := context.Background()
event, err := agent.eventModel.Save(ctx, info.userId, model.Events{
Name: args.Name,
})
if err != nil {
return event, err
}
locationId, err := uuid.Parse(args.LocationId)
if err != nil {
return event, err
}
return agent.eventModel.UpdateLocation(ctx, event.ID, locationId)
},
}
agent.toolHandler = toolHandler
return agent, nil

View File

@ -167,12 +167,12 @@ func main() {
return
}
err = eventModel.SaveToImage(ctx, userImage.ImageID, imageInfo.Events)
if err != nil {
log.Println("Failed to save events")
log.Println(err)
return
}
// err = eventModel.SaveToImage(ctx, userImage.ImageID, imageInfo.Events)
// if err != nil {
// log.Println("Failed to save events")
// log.Println(err)
// return
// }
}()
}
}

View File

@ -3,7 +3,7 @@ package models
import (
"context"
"database/sql"
"log"
. "github.com/go-jet/jet/v2/postgres"
"screenmark/screenmark/.gen/haystack/haystack/model"
. "screenmark/screenmark/.gen/haystack/haystack/table"
@ -14,49 +14,32 @@ type EventModel struct {
dbPool *sql.DB
}
// This looks stupid
func getEventValues(event model.Events) []any {
arr := make([]any, 0)
if event.Description != nil {
arr = append(arr, *event.Description)
} else {
arr = append(arr, nil)
}
if event.LocationID != nil {
arr = append(arr, *event.LocationID)
} else {
arr = append(arr, nil)
}
return arr
}
func (m EventModel) Save(ctx context.Context, events []model.Events) (model.Events, error) {
func (m EventModel) Save(ctx context.Context, userId uuid.UUID, event model.Events) (model.Events, error) {
// TODO tx here
insertEventStmt := Events.
INSERT(Events.Name, Events.Description)
for _, event := range events {
insertEventStmt = insertEventStmt.VALUES(event.Name, getEventValues(event)...)
}
insertEventStmt = insertEventStmt.RETURNING(Events.AllColumns)
log.Println(insertEventStmt.DebugSql())
INSERT(Events.Name, Events.Description).
VALUES(event.Name, event.Description).
RETURNING(Events.AllColumns)
insertedEvent := model.Events{}
err := insertEventStmt.QueryContext(ctx, m.dbPool, &insertedEvent)
if err != nil {
return insertedEvent, err
}
func (m EventModel) SaveToImage(ctx context.Context, imageId uuid.UUID, events []model.Events) error {
if len(events) == 0 {
return nil
insertUserEventStmt := UserEvents.
INSERT(UserEvents.UserID, UserEvents.EventID).
VALUES(userId, insertedEvent.ID).
RETURNING(UserEvents.AllColumns)
_, err = insertUserEventStmt.ExecContext(ctx, m.dbPool)
return insertedEvent, err
}
event, err := m.Save(ctx, events)
func (m EventModel) SaveToImage(ctx context.Context, userId uuid.UUID, imageId uuid.UUID, event model.Events) error {
event, err := m.Save(ctx, userId, event)
if err != nil {
return err
@ -71,6 +54,19 @@ func (m EventModel) SaveToImage(ctx context.Context, imageId uuid.UUID, events [
return err
}
func (m EventModel) UpdateLocation(ctx context.Context, eventId uuid.UUID, locationId uuid.UUID) (model.Events, error) {
updateEventLocationStmt := Events.
UPDATE(Events.LocationID).
SET(locationId).
WHERE(Events.ID.EQ(UUID(eventId))).
RETURNING(Events.AllColumns)
updatedEvent := model.Events{}
err := updateEventLocationStmt.QueryContext(ctx, m.dbPool, &updatedEvent)
return updatedEvent, err
}
func NewEventModel(db *sql.DB) EventModel {
return EventModel{dbPool: db}
}

View File

@ -3,7 +3,6 @@ package models
import (
"context"
"database/sql"
"log"
"screenmark/screenmark/.gen/haystack/haystack/model"
. "screenmark/screenmark/.gen/haystack/haystack/table"
@ -57,7 +56,7 @@ func (m LocationModel) SaveToImage(ctx context.Context, imageId uuid.UUID, locat
VALUES(imageId, locationId)
imageLocation := model.ImageLocations{}
_, err := insertImageLocationStmt.ExecContext(ctx, m.dbPool)
err := insertImageLocationStmt.QueryContext(ctx, m.dbPool, &imageLocation)
return imageLocation, err
}

View File

@ -86,6 +86,12 @@ CREATE TABLE haystack.image_events (
image_id UUID NOT NULL REFERENCES haystack.image (id)
);
CREATE TABLE haystack.user_events (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
event_id UUID NOT NULL REFERENCES haystack.events (id),
user_id UUID NOT NULL REFERENCES haystack.users (id)
);
/* -----| Indexes |----- */
CREATE INDEX user_tags_index ON haystack.user_tags(tag);