add line.linestate to DB. switch linetype to a dbmappable. update line sql queries. add state to /line:set command. bump migration (#2)

This commit is contained in:
sawka 2023-09-01 15:21:35 -07:00 committed by GitHub
parent 22fb034cc4
commit 5d89e0cfef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 76 deletions

View File

@ -0,0 +1 @@
ALTER TABLE line DROP COLUMN linestate;

View File

@ -0,0 +1 @@
ALTER TABLE line ADD COLUMN linestate json NOT NULL DEFAULT '{}';

View File

@ -68,7 +68,6 @@ CREATE TABLE history (
remoteid varchar(36) NOT NULL,
remotename varchar(50) NOT NULL,
haderror boolean NOT NULL,
cmdid varchar(36) NOT NULL,
cmdstr text NOT NULL,
ismetacmd boolean,
incognito boolean
@ -160,36 +159,13 @@ CREATE TABLE IF NOT EXISTS "line" (
linetype varchar(10) NOT NULL,
linelocal boolean NOT NULL,
text text NOT NULL,
cmdid varchar(36) NOT NULL,
ephemeral boolean NOT NULL,
contentheight int NOT NULL,
star int NOT NULL,
archived boolean NOT NULL,
renderer varchar(50) NOT NULL,
renderer varchar(50) NOT NULL, linestate json NOT NULL DEFAULT '{}',
PRIMARY KEY (screenid, lineid)
);
CREATE TABLE IF NOT EXISTS "cmd" (
screenid varchar(36) NOT NULL,
cmdid varchar(36) NOT NULL,
remoteownerid varchar(36) NOT NULL,
remoteid varchar(36) NOT NULL,
remotename varchar(50) NOT NULL,
cmdstr text NOT NULL,
rawcmdstr text NOT NULL,
festate json NOT NULL,
statebasehash varchar(36) NOT NULL,
statediffhasharr json NOT NULL,
termopts json NOT NULL,
origtermopts json NOT NULL,
status varchar(10) NOT NULL,
startpk json NOT NULL,
doneinfo json NOT NULL,
runout json NOT NULL,
rtnstate boolean NOT NULL,
rtnbasehash varchar(36) NOT NULL,
rtndiffhasharr json NOT NULL,
PRIMARY KEY (screenid, cmdid)
);
CREATE TABLE screenupdate (
updateid integer PRIMARY KEY,
screenid varchar(36) NOT NULL,
@ -204,3 +180,40 @@ CREATE TABLE webptypos (
PRIMARY KEY (screenid, lineid)
);
CREATE INDEX idx_screenupdate_ids ON screenupdate (screenid, lineid);
CREATE TABLE cmd_migration (
screenid varchar(36) NOT NULL,
lineid varchar(36) NOT NULL,
cmdid varchar(36) NOT NULL,
PRIMARY KEY (screenid, lineid)
);
CREATE TABLE IF NOT EXISTS "cmd" (
screenid varchar(36) NOT NULL,
lineid varchar(36) NOT NULL,
remoteownerid varchar(36) NOT NULL,
remoteid varchar(36) NOT NULL,
remotename varchar(50) NOT NULL,
cmdstr text NOT NULL,
rawcmdstr text NOT NULL,
festate json NOT NULL,
statebasehash varchar(36) NOT NULL,
statediffhasharr json NOT NULL,
termopts json NOT NULL,
origtermopts json NOT NULL,
status varchar(10) NOT NULL,
cmdpid int NOT NULL,
remotepid int NOT NULL,
donets bigint NOT NULL,
exitcode int NOT NULL,
durationms int NOT NULL,
rtnstate boolean NOT NULL,
rtnbasehash varchar(36) NOT NULL,
rtndiffhasharr json NOT NULL,
runout json NOT NULL,
PRIMARY KEY (screenid, lineid)
);
CREATE TABLE cmd_migrate20 (
screenid varchar(36) NOT NULL,
lineid varchar(36) NOT NULL,
cmdid varchar(36) NOT NULL,
PRIMARY KEY (screenid, lineid)
);

View File

@ -5,6 +5,7 @@ import (
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io/fs"
"log"
@ -23,6 +24,7 @@ import (
"github.com/commandlinedev/apishell/pkg/packet"
"github.com/commandlinedev/apishell/pkg/shexec"
"github.com/commandlinedev/prompt-server/pkg/comp"
"github.com/commandlinedev/prompt-server/pkg/dbutil"
"github.com/commandlinedev/prompt-server/pkg/pcloud"
"github.com/commandlinedev/prompt-server/pkg/remote"
"github.com/commandlinedev/prompt-server/pkg/remote/openai"
@ -63,6 +65,7 @@ const TsFormatStr = "2006-01-02 15:04:05"
const (
KwArgRenderer = "renderer"
KwArgView = "view"
KwArgState = "state"
)
var ColorNames = []string{"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "orange"}
@ -2604,8 +2607,23 @@ func LineSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto
}
varsUpdated = append(varsUpdated, KwArgView)
}
if stateJson, found := pk.Kwargs[KwArgState]; found {
if len(stateJson) > sstore.MaxLineStateSize {
return nil, fmt.Errorf("invalid state value (too large), size[%d], max[%d]", len(stateJson), sstore.MaxLineStateSize)
}
var stateMap map[string]any
err = json.Unmarshal([]byte(stateJson), &stateMap)
if err != nil {
return nil, fmt.Errorf("invalid state value, cannot parse json: %v", err)
}
err = sstore.UpdateLineState(ctx, ids.ScreenId, lineId, stateMap)
if err != nil {
return nil, fmt.Errorf("cannot update linestate: %v", err)
}
varsUpdated = append(varsUpdated, KwArgState)
}
if len(varsUpdated) == 0 {
return nil, fmt.Errorf("/line:set requires a value to set: %s", formatStrs([]string{KwArgView}, "or", false))
return nil, fmt.Errorf("/line:set requires a value to set: %s", formatStrs([]string{KwArgView, KwArgState}, "or", false))
}
updatedLine, err := sstore.GetLineById(ctx, ids.ScreenId, lineId)
if err != nil {
@ -2836,7 +2854,7 @@ func LineStarCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sst
if starVal > 5 {
return nil, fmt.Errorf("/line:star invalid star-value must be in the range of 0-5")
}
err = sstore.UpdateLineStar(ctx, lineId, starVal)
err = sstore.UpdateLineStar(ctx, ids.ScreenId, lineId, starVal)
if err != nil {
return nil, fmt.Errorf("/line:star error updating star value: %v", err)
}
@ -2991,6 +3009,11 @@ func LineShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sst
buf.WriteString(fmt.Sprintf(" %-15s %dms\n", "duration", cmd.DurationMs))
}
}
stateStr := dbutil.QuickJson(line.LineState)
if len(stateStr) > 80 {
stateStr = stateStr[0:77] + "..."
}
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "state", stateStr))
update := &sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("line %d info", line.LineNum),

View File

@ -58,11 +58,11 @@ func GetMapGen[PT MapConverterPtr[T], T any](tx *txwrap.TxWrap, query string, ar
}
func GetMappable[PT DBMappablePtr[T], T any](tx *txwrap.TxWrap, query string, args ...interface{}) PT {
rtn := PT(new(T))
m := tx.GetMap(query, args...)
if len(m) == 0 {
return nil
}
rtn := PT(new(T))
FromDBMap(rtn, m)
return rtn
}

View File

@ -339,6 +339,9 @@ func makeWebShareUpdate(ctx context.Context, update *sstore.ScreenUpdateType) (*
rtn.PtyData = &WebSharePtyData{PtyPos: realOffset, Data: data, Eof: true}
}
case sstore.UpdateType_LineState:
// TODO implement!
default:
return nil, fmt.Errorf("unsupported update type (pcloud/makeWebScreenUpdate): %s\n", update.UpdateType)
}

View File

@ -471,7 +471,7 @@ func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType,
return nil, nil
}
query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum`
tx.Select(&screen.Lines, query, screen.ScreenId)
screen.Lines = dbutil.SelectMappable[*LineType](tx, query, screen.ScreenId)
query = `SELECT * FROM cmd WHERE screenid = ?`
screen.Cmds = dbutil.SelectMapsGen[*CmdType](tx, query, screen.ScreenId)
return screen, nil
@ -761,16 +761,15 @@ func FindLineIdByArg(ctx context.Context, screenId string, lineArg string) (stri
func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*LineType, *CmdType, error) {
return WithTxRtn3(ctx, func(tx *TxWrap) (*LineType, *CmdType, error) {
var lineVal LineType
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
found := tx.Get(&lineVal, query, screenId, lineId)
if !found {
lineVal := dbutil.GetMappable[*LineType](tx, query, screenId, lineId)
if lineVal == nil {
return nil, nil, nil
}
var cmdRtn *CmdType
query = `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
cmdRtn = dbutil.GetMapGen[*CmdType](tx, query, screenId, lineId)
return &lineVal, cmdRtn, nil
return lineVal, cmdRtn, nil
})
}
@ -795,9 +794,9 @@ func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error {
query = `SELECT nextlinenum FROM screen WHERE screenid = ?`
nextLineNum := tx.GetInt(query, line.ScreenId)
line.LineNum = int64(nextLineNum)
query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, text, renderer, ephemeral, contentheight, star, archived)
VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:text,:renderer,:ephemeral,:contentheight,:star,:archived)`
tx.NamedExec(query, line)
query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, linestate, text, renderer, ephemeral, contentheight, star, archived)
VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:linestate,:text,:renderer,:ephemeral,:contentheight,:star,:archived)`
tx.NamedExec(query, dbutil.ToDBMap(line, false))
query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?`
tx.Exec(query, nextLineNum+1, line.ScreenId)
if cmd != nil {
@ -1889,10 +1888,10 @@ func GetFullState(ctx context.Context, ssPtr ShellStatePtr) (*packet.ShellState,
return state, nil
}
func UpdateLineStar(ctx context.Context, lineId string, starVal int) error {
func UpdateLineStar(ctx context.Context, screenId string, lineId string, starVal int) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE line SET star = ? WHERE lineid = ?`
tx.Exec(query, starVal, lineId)
query := `UPDATE line SET star = ? WHERE screenid = ? AND lineid = ?`
tx.Exec(query, starVal, screenId, lineId)
return nil
})
if txErr != nil {
@ -1903,8 +1902,8 @@ func UpdateLineStar(ctx context.Context, lineId string, starVal int) error {
func UpdateLineHeight(ctx context.Context, screenId string, lineId string, heightVal int) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE line SET contentheight = ? WHERE lineid = ?`
tx.Exec(query, heightVal, lineId)
query := `UPDATE line SET contentheight = ? WHERE screenid = ? AND lineid = ?`
tx.Exec(query, heightVal, screenId, lineId)
if isWebShare(tx, screenId) {
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineContentHeight)
}
@ -1918,8 +1917,8 @@ func UpdateLineHeight(ctx context.Context, screenId string, lineId string, heigh
func UpdateLineRenderer(ctx context.Context, screenId string, lineId string, renderer string) error {
return WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE line SET renderer = ? WHERE lineid = ?`
tx.Exec(query, renderer, lineId)
query := `UPDATE line SET renderer = ? WHERE screenid = ? AND lineid = ?`
tx.Exec(query, renderer, screenId, lineId)
if isWebShare(tx, screenId) {
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineRenderer)
}
@ -1927,28 +1926,34 @@ func UpdateLineRenderer(ctx context.Context, screenId string, lineId string, ren
})
}
// can return nil, nil if line is not found
func GetLineById(ctx context.Context, screenId string, lineId string) (*LineType, error) {
var rtn *LineType
txErr := WithTx(ctx, func(tx *TxWrap) error {
var line LineType
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
found := tx.Get(&line, query, screenId, lineId)
if found {
rtn = &line
func UpdateLineState(ctx context.Context, screenId string, lineId string, lineState map[string]any) error {
qjs := dbutil.QuickJson(lineState)
if len(qjs) > MaxLineStateSize {
return fmt.Errorf("linestate for line[%s:%s] exceeds maxsize, size[%d] max[%d]", screenId, lineId, len(qjs), MaxLineStateSize)
}
return WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE line SET linestate = ? WHERE screenid = ? AND lineid = ?`
tx.Exec(query, qjs, screenId, lineId)
if isWebShare(tx, screenId) {
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineState)
}
return nil
})
if txErr != nil {
return nil, txErr
}
return rtn, nil
}
// can return nil, nil if line is not found
func GetLineById(ctx context.Context, screenId string, lineId string) (*LineType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*LineType, error) {
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
line := dbutil.GetMappable[*LineType](tx, query, screenId, lineId)
return line, nil
})
}
func SetLineArchivedById(ctx context.Context, screenId string, lineId string, archived bool) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `UPDATE line SET archived = ? WHERE lineid = ?`
tx.Exec(query, archived, lineId)
query := `UPDATE line SET archived = ? WHERE screenid = ? AND lineid = ?`
tx.Exec(query, archived, screenId, lineId)
if isWebShare(tx, screenId) {
if archived {
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineDel)
@ -2395,10 +2400,9 @@ func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryIte
return nil, nil, nil
}
return WithTxRtn3(ctx, func(tx *TxWrap) ([]*LineType, []*CmdType, error) {
var lineArr []*LineType
lineIdsJsonArr := quickJsonArr(getLineIdsFromHistoryItems(historyItems))
query := `SELECT * FROM line WHERE lineid IN (SELECT value FROM json_each(?))`
tx.Select(&lineArr, query, lineIdsJsonArr)
lineArr := dbutil.SelectMappable[*LineType](tx, query, lineIdsJsonArr)
query = `SELECT * FROM cmd WHERE lineid IN (SELECT value FROM json_each(?))`
cmdArr := dbutil.SelectMapsGen[*CmdType](tx, query, lineIdsJsonArr)
return lineArr, cmdArr, nil

View File

@ -17,7 +17,7 @@ import (
"github.com/golang-migrate/migrate/v4"
)
const MaxMigration = 20
const MaxMigration = 21
const MigratePrimaryScreenVersion = 9
const CmdScreenSpecialMigration = 13
const CmdLineSpecialMigration = 20

View File

@ -34,6 +34,7 @@ const DBFileName = "prompt.db"
const DBFileNameBackup = "backup.prompt.db"
const MaxWebShareLineCount = 50
const MaxWebShareScreenCount = 3
const MaxLineStateSize = 4 * 1024 // 4k for now, can raise if needed
const DefaultSessionName = "default"
const LocalRemoteAlias = "local"
@ -113,6 +114,7 @@ const (
UpdateType_LineDel = "line:del"
UpdateType_LineRenderer = "line:renderer"
UpdateType_LineContentHeight = "line:contentheight"
UpdateType_LineState = "line:state"
UpdateType_CmdStatus = "cmd:status"
UpdateType_CmdTermOpts = "cmd:termopts"
UpdateType_CmdExitCode = "cmd:exitcode"
@ -707,23 +709,26 @@ type ScreenUpdateType struct {
func (ScreenUpdateType) UseDBMap() {}
type LineType struct {
ScreenId string `json:"screenid"`
UserId string `json:"userid"`
LineId string `json:"lineid"`
Ts int64 `json:"ts"`
LineNum int64 `json:"linenum"`
LineNumTemp bool `json:"linenumtemp,omitempty"`
LineLocal bool `json:"linelocal"`
LineType string `json:"linetype"`
Renderer string `json:"renderer,omitempty"`
Text string `json:"text,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
ContentHeight int64 `json:"contentheight,omitempty"`
Star bool `json:"star,omitempty"`
Archived bool `json:"archived,omitempty"`
Remove bool `json:"remove,omitempty"`
ScreenId string `json:"screenid"`
UserId string `json:"userid"`
LineId string `json:"lineid"`
Ts int64 `json:"ts"`
LineNum int64 `json:"linenum"`
LineNumTemp bool `json:"linenumtemp,omitempty"`
LineLocal bool `json:"linelocal"`
LineType string `json:"linetype"`
LineState map[string]any `json:"linestate"`
Renderer string `json:"renderer,omitempty"`
Text string `json:"text,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
ContentHeight int64 `json:"contentheight,omitempty"`
Star bool `json:"star,omitempty"`
Archived bool `json:"archived,omitempty"`
Remove bool `json:"remove,omitempty"`
}
func (LineType) UseDBMap() {}
type OpenAIUsage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`