Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions frontend/app/aipanel/aipanelinput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface AIPanelInputProps {
export interface AIPanelInputRef {
focus: () => void;
resize: () => void;
scrollToBottom: () => void;
}

export const AIPanelInput = memo(({ onSubmit, status, model }: AIPanelInputProps) => {
Expand All @@ -43,6 +44,12 @@ export const AIPanelInput = memo(({ onSubmit, status, model }: AIPanelInputProps
textareaRef.current?.focus();
},
resize: resizeTextarea,
scrollToBottom: () => {
const textarea = textareaRef.current;
if (textarea) {
textarea.scrollTop = textarea.scrollHeight;
}
},
},
};
model.registerInputRef(inputRefObject);
Expand Down
6 changes: 5 additions & 1 deletion frontend/app/aipanel/waveai-model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ export class WaveAIModel {
return input != null && input.trim().length > 0;
}

appendText(text: string, newLine?: boolean) {
appendText(text: string, newLine?: boolean, opts?: { scrollToBottom?: boolean }) {
const currentInput = globalStore.get(this.inputAtom);
let newInput = currentInput;

Expand All @@ -317,6 +317,10 @@ export class WaveAIModel {

newInput += text;
globalStore.set(this.inputAtom, newInput);

if (opts?.scrollToBottom && this.inputRef?.current) {
setTimeout(() => this.inputRef.current.scrollToBottom(), 10);
}
}

setModel(model: string) {
Expand Down
25 changes: 24 additions & 1 deletion frontend/builder/builder-buildpanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,24 @@ const BuilderBuildPanel = memo(() => {
BuilderAppPanelModel.getInstance().restartBuilder();
}, []);

const filteredLines = showDebug ? outputLines : outputLines.filter((line) => !line.startsWith("[debug]") && line.trim().length > 0);
const handleSendToAI = useCallback(() => {
const currentShowDebug = globalStore.get(model.showDebug);
const currentOutputLines = globalStore.get(model.outputLines);
const filtered = currentShowDebug
? currentOutputLines
: currentOutputLines.filter((line) => !line.startsWith("[debug]") && line.trim().length > 0);

const linesToSend = filtered.slice(-200);
const text = linesToSend.join("\n");
const aiModel = WaveAIModel.getInstance();
const formattedText = `from builder output:\n\`\`\`\n${text}\n\`\`\`\n`;
aiModel.appendText(formattedText, true, { scrollToBottom: true });
aiModel.focusInput();
}, [model]);

const filteredLines = showDebug
? outputLines
: outputLines.filter((line) => !line.startsWith("[debug]") && line.trim().length > 0);

return (
<div className="w-full h-full flex flex-col bg-black rounded-br-2">
Expand All @@ -98,6 +115,12 @@ const BuilderBuildPanel = memo(() => {
/>
Debug
</label>
<button
className="px-3 py-1 text-sm font-medium rounded transition-colors bg-accent/80 text-white hover:bg-accent cursor-pointer"
onClick={handleSendToAI}
>
Send Output to AI
</button>
<button
className="px-3 py-1 text-sm font-medium rounded transition-colors bg-accent/80 text-white hover:bg-accent cursor-pointer"
onClick={handleRestart}
Expand Down
7 changes: 4 additions & 3 deletions frontend/builder/tabs/builder-configdatatab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const BuilderConfigDataTab = memo(() => {
const model = BuilderAppPanelModel.getInstance();
const builderStatus = useAtomValue(model.builderStatusAtom);
const builderId = useAtomValue(atoms.builderId);
const activeTab = useAtomValue(model.activeTab);
const [state, setState] = useState<ConfigDataState>({
config: null,
data: null,
Expand Down Expand Up @@ -144,17 +145,17 @@ const BuilderConfigDataTab = memo(() => {
}, [state.data]);

useEffect(() => {
if (isRunning) {
if (activeTab === "configdata" && isRunning) {
fetchData();
} else {
} else if (!isRunning) {
setState({
config: null,
data: null,
error: null,
isLoading: false,
});
}
}, [isRunning, fetchData]);
}, [activeTab, isRunning, fetchData]);

if (!isRunning) {
return <NotRunningView />;
Expand Down
2 changes: 2 additions & 0 deletions frontend/types/gotypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,8 @@ declare global {
"count:views"?: {[key: string]: number};
"waveai:apitype"?: string;
"waveai:model"?: string;
"waveai:chatid"?: string;
"waveai:stepnum"?: number;
"waveai:inputtokens"?: number;
"waveai:outputtokens"?: number;
"waveai:nativewebsearchcount"?: number;
Expand Down
30 changes: 22 additions & 8 deletions pkg/aiusechat/anthropic/anthropic-convertmessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,32 @@ func buildAnthropicHTTPRequest(ctx context.Context, msgs []anthropicInputMessage
}
}

// inject chatOpts.AppGoFile as a "text" block at the END of the LAST "user" message found (append to Content)
if chatOpts.AppGoFile != "" {
// inject chatOpts.PlatformInfo, AppStaticFiles, and AppGoFile as "text" blocks at the END of the LAST "user" message found (append to Content)
if chatOpts.PlatformInfo != "" || chatOpts.AppStaticFiles != "" || chatOpts.AppGoFile != "" {
// Find the last "user" message
for i := len(convertedMsgs) - 1; i >= 0; i-- {
if convertedMsgs[i].Role == "user" {
// Create a text block with the AppGoFile content wrapped in XML tag
appGoFileBlock := anthropicMessageContentBlock{
Type: "text",
Text: "<CurrentAppGoFile>\n" + chatOpts.AppGoFile + "\n</CurrentAppGoFile>",
if chatOpts.PlatformInfo != "" {
platformInfoBlock := anthropicMessageContentBlock{
Type: "text",
Text: "<PlatformInfo>\n" + chatOpts.PlatformInfo + "\n</PlatformInfo>",
}
convertedMsgs[i].Content = append(convertedMsgs[i].Content, platformInfoBlock)
}
if chatOpts.AppStaticFiles != "" {
appStaticFilesBlock := anthropicMessageContentBlock{
Type: "text",
Text: "<CurrentAppStaticFiles>\n" + chatOpts.AppStaticFiles + "\n</CurrentAppStaticFiles>",
}
convertedMsgs[i].Content = append(convertedMsgs[i].Content, appStaticFilesBlock)
}
if chatOpts.AppGoFile != "" {
appGoFileBlock := anthropicMessageContentBlock{
Type: "text",
Text: "<CurrentAppGoFile>\n" + chatOpts.AppGoFile + "\n</CurrentAppGoFile>",
}
convertedMsgs[i].Content = append(convertedMsgs[i].Content, appGoFileBlock)
}
// Append to the Content of this message
convertedMsgs[i].Content = append(convertedMsgs[i].Content, appGoFileBlock)
break
}
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/aiusechat/openai/openai-convertmessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ func buildOpenAIHTTPRequest(ctx context.Context, inputs []any, chatOpts uctypes.
if chatOpts.TabState != "" {
appendToLastUserMessage(inputs, chatOpts.TabState)
}
if chatOpts.PlatformInfo != "" {
appendToLastUserMessage(inputs, "<PlatformInfo>\n"+chatOpts.PlatformInfo+"\n</PlatformInfo>")
}
if chatOpts.AppStaticFiles != "" {
appendToLastUserMessage(inputs, "<CurrentAppStaticFiles>\n"+chatOpts.AppStaticFiles+"\n</CurrentAppStaticFiles>")
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/aiusechat/uctypes/usechat-types.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ type WaveChatOpts struct {
Tools []ToolDefinition
SystemPrompt []string
TabStateGenerator func() (string, []ToolDefinition, string, error)
BuilderAppGenerator func() (string, string, error)
BuilderAppGenerator func() (string, string, string, error)
WidgetAccess bool
RegisterToolApproval func(string)
AllowNativeWebSearch bool
Expand All @@ -462,6 +462,7 @@ type WaveChatOpts struct {
TabId string
AppGoFile string
AppStaticFiles string
PlatformInfo string
}

func (opts *WaveChatOpts) GetToolDefinition(toolName string) *ToolDefinition {
Expand Down
17 changes: 13 additions & 4 deletions pkg/aiusechat/usechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"log"
"net/http"
"os"
"os/user"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -411,10 +412,11 @@ func RunAIChat(ctx context.Context, sseHandler *sse.SSEHandlerCh, backend UseCha
}
}
if chatOpts.BuilderAppGenerator != nil {
appGoFile, appStaticFiles, appErr := chatOpts.BuilderAppGenerator()
appGoFile, appStaticFiles, platformInfo, appErr := chatOpts.BuilderAppGenerator()
if appErr == nil {
chatOpts.AppGoFile = appGoFile
chatOpts.AppStaticFiles = appStaticFiles
chatOpts.PlatformInfo = platformInfo
}
}
stopReason, rtnMessage, err := runAIChatStep(ctx, sseHandler, backend, chatOpts, cont)
Expand Down Expand Up @@ -689,7 +691,7 @@ func WaveAIPostMessageHandler(w http.ResponseWriter, r *http.Request) {
}

if req.BuilderAppId != "" {
chatOpts.BuilderAppGenerator = func() (string, string, error) {
chatOpts.BuilderAppGenerator = func() (string, string, string, error) {
return generateBuilderAppData(req.BuilderAppId)
}
}
Expand Down Expand Up @@ -830,7 +832,7 @@ type StaticFileInfo struct {
ModifiedTime string `json:"modified_time"`
}

func generateBuilderAppData(appId string) (string, string, error) {
func generateBuilderAppData(appId string) (string, string, string, error) {
appGoFile := ""
fileData, err := waveappstore.ReadAppFile(appId, "app.go")
if err == nil {
Expand Down Expand Up @@ -860,5 +862,12 @@ func generateBuilderAppData(appId string) (string, string, error) {
}
}

return appGoFile, staticFilesJSON, nil
platformInfo := wavebase.GetSystemSummary()
if currentUser, userErr := user.Current(); userErr == nil && currentUser.Username != "" {
platformInfo = fmt.Sprintf("Local Machine: %s, User: %s", platformInfo, currentUser.Username)
} else {
platformInfo = fmt.Sprintf("Local Machine: %s", platformInfo)
}

return appGoFile, staticFilesJSON, platformInfo, nil
}
24 changes: 13 additions & 11 deletions pkg/buildercontroller/buildercontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,19 @@ func (bc *BuilderController) buildAndRun(ctx context.Context, appId string, buil
return
}

process, err := bc.runBuilderApp(ctx, appId, cachePath, builderEnv)
if err != nil {
bc.handleBuildError(fmt.Errorf("failed to run app: %w", err), resultCh)
return
}

bc.lock.Lock()
bc.process = process
bc.setStatus_nolock(BuilderStatus_Running, process.Port, 0, "")
bc.lock.Unlock()

time.Sleep(1 * time.Second)

if resultCh != nil {
buildOutput := ""
if bc.outputBuffer != nil {
Expand All @@ -276,17 +289,6 @@ func (bc *BuilderController) buildAndRun(ctx context.Context, appId string, buil
}
}

process, err := bc.runBuilderApp(ctx, appId, cachePath, builderEnv)
if err != nil {
bc.handleBuildError(fmt.Errorf("failed to run app: %w", err), resultCh)
return
}

bc.lock.Lock()
bc.process = process
bc.setStatus_nolock(BuilderStatus_Running, process.Port, 0, "")
bc.lock.Unlock()

go func() {
<-process.WaitCh
bc.lock.Lock()
Expand Down
Loading