Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
933ba03
feat: For-each loop with branch
ramedina86 Mar 3, 2025
9558e00
feat: Max call stack
ramedina86 Mar 6, 2025
c962165
feat: Max call stack
ramedina86 Mar 8, 2025
1c4ff09
feat: Autogen
ramedina86 Mar 11, 2025
8c7ff64
feat: Tool calling POC
ramedina86 Mar 13, 2025
26eea80
feat: Max depth, refactor
ramedina86 Mar 13, 2025
b6f9095
fix: App process not available during test
ramedina86 Mar 13, 2025
677b522
fix: Handle none
ramedina86 Mar 13, 2025
bc5f24e
fix: Change tests to match base block syntax change
ramedina86 Mar 13, 2025
220d42a
fix: Mypy and ruff fixes
ramedina86 Mar 13, 2025
0700a0a
Merge pull request #774 from writer/feat-max-call-stack
ramedina86 Mar 13, 2025
2e3c342
Merge branch 'dev' into feat-foreach-branch
ramedina86 Mar 13, 2025
9ef77ec
feat: Foreach branching
ramedina86 Mar 13, 2025
0221ce9
fix: Foreach tests
ramedina86 Mar 13, 2025
a75262c
Merge pull request #776 from writer/feat-foreach-branch
ramedina86 Mar 13, 2025
681038c
Merge branch 'dev' into feat-autogen
ramedina86 Mar 14, 2025
76cb5e9
fix: check for "workflows" mode to trigger mode change during page ch…
mmikita95 Mar 18, 2025
ddadce4
Merge pull request #779 from mmikita95/fix-change-page-mode-switch
ramedina86 Mar 19, 2025
eea86de
feat: Autogen
ramedina86 Mar 19, 2025
60fbf78
fix: Remove unnecessary header
ramedina86 Mar 19, 2025
2e12b81
fix: Mypy
ramedina86 Mar 19, 2025
e0745a3
chore: Bump version
ramedina86 Mar 19, 2025
82d12ff
Merge pull request #780 from writer/feat-autogen
ramedina86 Mar 19, 2025
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "writer"
version = "0.8.3rc9"
version = "0.8.3rc10"
description = "An open-source, Python framework for building feature-rich apps that are fully integrated with the Writer platform."
authors = ["Writer, Inc."]
readme = "README.md"
Expand Down
92 changes: 77 additions & 15 deletions src/ui/src/builder/panels/BuilderLogWorkflowExecutionTrace.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
<template>
<div class="BuiderLogWorkflowExecutionTrace">
<div v-if="trace.length > 0" class="trace">
<div
v-for="(entry, entryId) in trace"
:key="entryId"
class="traceEntry"
>
<div class="type">
<span v-if="entry.type == 'functionCall'">⚡️</span>
<span v-if="entry.type == 'reasoning'">🧠</span>
</div>
<div class="main">
<div v-if="entry.thought">
<strong>Thought.</strong> {{ entry.thought }}
</div>
<div v-if="entry.action">
<strong>Action.</strong> {{ entry.action }}
</div>
<div v-if="entry.name">
{{ entry.name }}
</div>
<div v-if="entry.parameters">
<SharedJsonViewer
:hide-root="true"
:data="entry.parameters"
:initial-depth="1"
class="data"
/>
</div>
</div>
</div>
</div>
<div class="details">
<div>
<h3>Result</h3>
Expand Down Expand Up @@ -55,10 +86,10 @@
</div>
</div>

<div class="trace">
<div class="callStack">
<h3>Call stack</h3>
<div
v-for="(component, componentId) in trace"
v-for="(component, componentId) in callStack"
:key="componentId"
class="component"
:class="{
Expand Down Expand Up @@ -118,15 +149,17 @@ const props = defineProps<{
executionItem: WorkflowExecutionLog["summary"][number];
}>();

const trace = computed(() => {
const traceArr: Component["id"][] =
props.executionItem.executionEnvironment?.["trace"];
const callStack = computed(() => {
const callStackArr: Component["id"][] =
props.executionItem.executionEnvironment?.["call_stack"] ?? [];

const trace = Object.fromEntries(
traceArr.map((cid) => [cid, wf.getComponentById(cid)]),
return Object.fromEntries(
callStackArr.map((cid) => [cid, wf.getComponentById(cid)]),
);
});

return trace;
const trace = computed(() => {
return props.executionItem.executionEnvironment?.["trace"] ?? [];
});

async function selectBlock(componentId: Component["id"]) {
Expand All @@ -143,6 +176,7 @@ async function selectBlock(componentId: Component["id"]) {
.BuiderLogWorkflowExecutionTrace {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 48px;
}

Expand Down Expand Up @@ -174,34 +208,62 @@ h3 {
}

.environment {
flex: 0 0 30%;
}

.trace {
flex: 0 0 30%;
.callStack {
display: flex;
flex-direction: column;
gap: 8px;
grid-template-columns: 3 / 3;
}

.trace .component {
.callStack .component {
display: flex;
align-items: center;
border-radius: 8px;
padding: 8px;
border: 1px solid var(--builderSeparatorColor);
}

.trace .component > div {
.callStack .component > div {
flex: 1 0 auto;
}

.trace .component .eyebrow {
.callStack .component .eyebrow {
font-size: 12px;
color: var(--builderSecondaryTextColor);
}

.trace .component.active {
.callStack .component.active {
border-color: var(--builderSelectedColor);
}

.trace {
display: flex;
flex-direction: column;
gap: 8px;
grid-column-start: 1;
grid-column-end: 4;
}

.trace .traceEntry {
display: flex;
align-items: center;
border-radius: 8px;
border: 1px solid var(--builderSeparatorColor);
}

.trace .traceEntry .type {
flex: 0 0 72px;
font-size: 20px;
height: 100%;
align-items: center;
display: flex;
justify-content: center;
background: var(--builderSubtleSeparatorColor);
}

.trace .traceEntry .main {
padding: 8px;
}
</style>
4 changes: 3 additions & 1 deletion src/ui/src/builder/useComponentActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export function useComponentActions(wf: Core, ssbm: BuilderManager) {
parentId: Component["id"],
position?: number,
initProperties?: Partial<
Omit<Component, "id" | "type" | "parent" | "handlers" | "position">
Omit<Component, "type" | "parent" | "handlers" | "position">
>,
initializer?: (parentId: Component["id"]) => void,
): Component["id"] {
Expand All @@ -176,6 +176,7 @@ export function useComponentActions(wf: Core, ssbm: BuilderManager) {
position,
initProperties,
);
component.id = initProperties?.id ?? component.id;
const transactionId = `create-${component.id}`;
ssbm.openMutationTransaction(transactionId, `Create`);
wf.addComponent(component);
Expand Down Expand Up @@ -979,6 +980,7 @@ export function useComponentActions(wf: Core, ssbm: BuilderManager) {
}

return {
generateNewComponentId,
moveComponent,
moveComponentUp,
moveComponentDown,
Expand Down
142 changes: 142 additions & 0 deletions src/ui/src/components/workflows/WorkflowsAutogen.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<template>
<div class="WorkflowsAutogen">
<template v-if="!isBusy">
<div class="main">
<WdsTextareaInput
v-model="prompt"
rows="8"
placeholder="Describe the blocks you'd like to generate..."
></WdsTextareaInput>
</div>
<div class="buttons">
<WdsButton @click="handleAutogen">
<i class="material-symbols-outlined">bolt</i> Generate
</WdsButton>
<WdsButton variant="tertiary" @click="handleCancel">
Cancel
</WdsButton>
</div>
</template>

<template v-else>
<WorkflowsLifeLoading></WorkflowsLifeLoading>
<h2>Generating...</h2>
</template>
</div>
</template>

<script setup lang="ts">
import { inject, ref } from "vue";
import WdsButton from "@/wds/WdsButton.vue";
import WdsTextareaInput from "@/wds/WdsTextareaInput.vue";
import WorkflowsLifeLoading from "./WorkflowsLifeLoading.vue";
import { Component } from "@/writerTypes";
import { useComponentActions } from "@/builder/useComponentActions";
import injectionKeys from "@/injectionKeys";

const wf = inject(injectionKeys.core);
const wfbm = inject(injectionKeys.builderManager);

const { generateNewComponentId } = useComponentActions(wf, wfbm);

const isBusy = ref(false);
const prompt = ref("");

const emits = defineEmits(["blockGeneration"]);

function handleCancel() {
emits("blockGeneration", null);
}

/**
* The generation happens with simple ids (aig1, aig2, ...).
* Component ids are altered to preserve uniqueness across blueprints.
*/
function alterIds(components: Component[]) {
const mapping: Record<string, string> = {};

// Switch ids

components.forEach((c) => {
const newId = generateNewComponentId();
mapping[c.id] = newId;
c.id = newId;
});

// Switch out ids

components.forEach((c) => {
c.outs?.forEach((out) => {
out.toNodeId = mapping[out.toNodeId];
});
});

// Switch results

components.forEach((c) => {
Object.entries(mapping).forEach(([originalId, newId]) => {
const regex = new RegExp(
`(@{\\s*results\\.)aig${originalId}\\b`,
"g",
);
Object.entries(c.content).forEach(([fieldKey, field]) => {
const newField = field.replace(regex, (_match, capturedAig) =>
_match.replace(capturedAig, newId),
);
c.content[fieldKey] = newField;
});
});
});

return components;
}

async function handleAutogen() {
const description = prompt.value;
isBusy.value = true;
const response = await fetch("/api/autogen", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ description }),
});

isBusy.value = false;

if (!response.ok) {
throw new Error(`Error: ${response.status} - ${response.statusText}`);
}

const data = await response.json(); // Assuming the response is JSON

const components: Component[] = alterIds(data.blueprint?.components);
emits("blockGeneration", { components });
}
</script>

<style scoped>
.WorkflowsAutogen {
display: flex;
gap: 24px;
flex-direction: column;
align-items: center;
}

.main {
width: 100%;
}

h2 {
margin: 0;
font-size: 24px;
font-style: normal;
font-weight: 500;
line-height: 160%;
}

.buttons {
display: flex;
gap: 16px;
}
</style>
Loading