Image input

This commit is contained in:
Ian Gulliver
2026-10-14 20:07:26 -07:00
parent bd8702ee87
commit 9e741ede32
27 changed files with 1044 additions and 122 deletions
+1 -1
View File
@@ -45,6 +45,6 @@ func (m *Manager) handoff(ctx context.Context, sessionID, handoff string) error
return err
}
_, err = m.create(ctx, task, creator, handoffPreamble+handoff, sess.Model, "byre")
_, err = m.create(ctx, task, creator, handoffPreamble+handoff, sess.Model, "byre", nil)
return err
}
+1 -1
View File
@@ -250,7 +250,7 @@ func (m *Manager) Deliver(ctx context.Context, sessionID string) error {
return err
}
_, err = m.turn(ctx, sess, task, pending[0].Response.User, deliveryMessage(pending))
_, err = m.turn(ctx, sess, task, pending[0].Response.User, deliveryMessage(pending), nil)
if errors.Is(err, ErrConflict) {
return nil
}
+29 -15
View File
@@ -83,11 +83,11 @@ func New(st *store.Store, limits Limits, worktreeRoot string) *Manager {
}
}
func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model string) (api.AgentSession, error) {
return m.create(ctx, task, creator, prompt, model, creator.Login)
func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model string, images ...string) (api.AgentSession, error) {
return m.create(ctx, task, creator, prompt, model, creator.Login, images)
}
func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, prompt, model, actor string) (api.AgentSession, error) {
func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, prompt, model, actor string, images []string) (api.AgentSession, error) {
if creator.GitName == "" || creator.GitEmail == "" {
return api.AgentSession{}, ErrNoGitIdentity
}
@@ -124,10 +124,10 @@ func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, p
return api.AgentSession{}, err
}
return m.turn(ctx, sess, task, actor, prompt)
return m.turn(ctx, sess, task, actor, prompt, images)
}
func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model string) (api.AgentSession, error) {
func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model string, images ...string) (api.AgentSession, error) {
sess, err := m.store.GetSession(ctx, sessionID)
if err != nil {
return api.AgentSession{}, err
@@ -150,7 +150,7 @@ func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model string
return api.AgentSession{}, err
}
return m.turn(ctx, sess, task, actor, text)
return m.turn(ctx, sess, task, actor, text, images)
}
func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSession, error) {
@@ -249,7 +249,7 @@ func (m *Manager) Recover(ctx context.Context) error {
continue
}
err = m.message(ctx, l, "byre", restartNotice)
err = m.message(ctx, l, "byre", restartNotice, nil)
if err != nil {
return err
}
@@ -310,7 +310,7 @@ func (m *Manager) Usage(ctx context.Context) (Usage, error) {
return u, nil
}
func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string) (api.AgentSession, error) {
func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string) (api.AgentSession, error) {
m.capMu.Lock()
defer m.capMu.Unlock()
@@ -320,7 +320,7 @@ func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task
}
if room {
return m.start(ctx, sess, task, actor, text, api.AgentSessionStateIdle)
return m.start(ctx, sess, task, actor, text, images, api.AgentSessionStateIdle)
}
err = transition(ctx, m.store, sess.Id, api.AgentSessionStateIdle, api.AgentSessionStateQueued, actor)
@@ -333,6 +333,7 @@ func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task
Vendor: sess.Vendor,
Actor: actor,
Text: text,
Images: images,
CreatedAt: time.Now().UTC(),
})
if err != nil {
@@ -342,7 +343,7 @@ func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task
return m.store.GetSession(ctx, sess.Id)
}
func (m *Manager) start(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, from api.AgentSessionState) (api.AgentSession, error) {
func (m *Manager) start(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string, from api.AgentSessionState) (api.AgentSession, error) {
err := transition(ctx, m.store, sess.Id, from, api.AgentSessionStateRunning, actor)
if err != nil {
return api.AgentSession{}, err
@@ -360,7 +361,7 @@ func (m *Manager) start(ctx context.Context, sess api.AgentSession, task api.Tas
return api.AgentSession{}, err
}
err = m.message(ctx, l, actor, text)
err = m.message(ctx, l, actor, text, images)
if err != nil {
return api.AgentSession{}, err
}
@@ -430,7 +431,7 @@ func (m *Manager) drain(ctx context.Context) error {
return err
}
_, err = m.start(ctx, sess, task, q.Actor, q.Text, api.AgentSessionStateQueued)
_, err = m.start(ctx, sess, task, q.Actor, q.Text, q.Images, api.AgentSessionStateQueued)
if err != nil {
slog.Error("start queued turn", "session", q.SessionID, "err", err)
}
@@ -467,12 +468,25 @@ func transition(ctx context.Context, st *store.Store, id string, from, to api.Ag
return err
}
func (m *Manager) message(ctx context.Context, l *live, actor, text string) error {
func (m *Manager) message(ctx context.Context, l *live, actor, text string, images []string) error {
imgs := []claude.Image{}
for _, id := range images {
b, data, err := m.store.ReadBlob(ctx, id)
if err != nil {
return err
}
imgs = append(imgs, claude.Image{MediaType: b.ContentType, Data: data})
}
head, err := git.Head(l.dir)
if err != nil {
return err
}
err = m.emit(ctx, l, actor, "user.message", map[string]any{"text": text, "head": head})
payload := map[string]any{"text": text, "head": head}
if len(images) > 0 {
payload["images"] = images
}
err = m.emit(ctx, l, actor, "user.message", payload)
if err != nil {
return err
}
@@ -485,7 +499,7 @@ func (m *Manager) message(ctx context.Context, l *live, actor, text string) erro
l.rateLimited = nil
m.mu.Unlock()
return l.sess.Send(text)
return l.sess.Send(text, imgs...)
}
func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task api.Task) (*live, error) {
+1 -1
View File
@@ -119,7 +119,7 @@ func (m *Manager) createTaskFromAgent(ctx context.Context, l *live, raw json.Raw
return "", err
}
_, err = m.create(ctx, task, creator, args.Prompt, model, agentActor)
_, err = m.create(ctx, task, creator, args.Prompt, model, agentActor, nil)
if err != nil {
return "", fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
}