From ea920631eff963c2ab63f70b4c770f99c090b137 Mon Sep 17 00:00:00 2001 From: Fabien Arcellier Date: Mon, 2 Dec 2024 10:31:02 +0100 Subject: [PATCH 1/6] docs: configures the documentation architecture for the local environment * fix: improve the documentation of seo section --- docs/framework/seo.mdx | 6 +++++- docs/mint.json | 31 ++++++++++++++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/framework/seo.mdx b/docs/framework/seo.mdx index 01af89993..085b6ed49 100644 --- a/docs/framework/seo.mdx +++ b/docs/framework/seo.mdx @@ -1,5 +1,5 @@ --- -title: "SEO" +title: "Seo and social sharing" mode: "wide" --- @@ -41,6 +41,7 @@ writer.serve.configure_webpage_metadata( You can also use a function to generate the meta tags dynamically. +*./server_setup.py* ```python def _meta(): last_news = db.get_last_news() @@ -57,6 +58,7 @@ writer.serve.configure_webpage_metadata(meta=_meta) When you share a link on social networks, they will try to fetch the metadata of the page to display a preview. +*./server_setup.py* ```python writer.serve.configure_webpage_metadata( opengraph_tags= { @@ -66,9 +68,11 @@ writer.serve.configure_webpage_metadata( "og:url": "https://myapp.com" } ) +``` You can also use a function to generate the opengraph tags dynamically. +*./server_setup.py* ```python def _opengraph_tags(): last_news = db.get_last_news() diff --git a/docs/mint.json b/docs/mint.json index 767ba68eb..246cf5839 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -32,23 +32,20 @@ "framework/introduction", "framework/quickstart", "framework/ai-module", - "framework/cloud-deploy", - "framework/sample-apps" + "framework/sample-apps", + "framework/component-list-link" ] }, { "group": "Guides", "pages": [ "framework/application-state", - "framework/event-handlers", + "framework/backend-initiated-actions", "framework/builder-basics", + "framework/event-handlers", "framework/handling-inputs", - "framework/dataframe", - "framework/backend-driven-ui", - "framework/stylesheets", - "framework/frontend-scripts", - "framework/custom-components", - "framework/authentication" + "framework/dataframe", + "framework/repeater" ] }, { @@ -64,19 +61,23 @@ "group": "Deployment", "pages": [ "framework/cloud-deploy", - "framework/deploy-with-docker", - "framework/testing" + "framework/deploy-with-docker" ] }, { "group": "Advanced", "pages": [ - "framework/repeater", - "framework/backend-initiated-actions", + "framework/authentication", + "framework/backend-driven-ui", + "framework/custom-components", + "framework/custom-server", + "framework/frontend-scripts", "framework/page-routes", "framework/sessions", - "framework/custom-server", - "framework/state-schema" + "framework/state-schema", + "framework/stylesheets", + "framework/testing", + "framework/seo" ] }, { From acffb4bb65a4254e06b0e4299490f5ff2d8a503a Mon Sep 17 00:00:00 2001 From: mmikita95 Date: Fri, 6 Dec 2024 11:25:40 +0300 Subject: [PATCH 2/6] chore(docs): document the usage of `ask` and `stream_ask` methods --- docs/framework/ai-module.mdx | 87 ++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/docs/framework/ai-module.mdx b/docs/framework/ai-module.mdx index 4754f5725..aab86f566 100644 --- a/docs/framework/ai-module.mdx +++ b/docs/framework/ai-module.mdx @@ -138,7 +138,7 @@ graph.add_file(file) # Remove the file from the graph graph.remove_file(file) -```` +``` #### Applying Graphs to Conversation completion @@ -228,10 +228,10 @@ response = conversation.complete(tools=tool, max_tool_depth=7) You can pass either a single tool or a list of tools to the `complete()` or `stream_complete()` methods. The tools can be a combination of FunctionTool, Graph, or JSON-defined tools. ```python -from writer.ai import FunctionTool, retrieve_graph +from writer.ai import create_function_tool, retrieve_graph # Define a function tool -tool1 = FunctionTool( +tool1 = create_function_tool( name="get_data", callable=lambda x: f"Data for {x}", parameters={"x": {"type": "string", "description": "Input value"}} @@ -245,7 +245,9 @@ response = conversation.complete(tools=[tool1, graph]) ``` ## Text generation without a conversation state -These `complete` and `stream_complete` methods are designed for one-off text generation without the need to manage a conversation state. They return the model's response as a string. Each function accepts a `config` dictionary allowing call-specific configurations. + +### Text generation against a string prompt +`complete` and `stream_complete` methods are designed for one-off text generation without the need to manage a conversation state. They return the model's response as a string. Each function accepts a `config` dictionary allowing call-specific configurations. ```python complete @@ -260,4 +262,81 @@ for text_chunk in stream_complete("Explore the benefits of AI.", config={'temper ``` +### Text generation against graphs +The `ask` and `stream_ask` methods allow you to query one or more graphs to generate responses from the information stored within them. + +#### Two approaches to questioning graphs + +There are two ways to query graphs, depending on your needs: +1. **Graph-Level Methods** (`Graph.ask`, `Graph.stream_ask`): Used when working with a single graph instance. These methods are tied directly to the Graph object, encapsulating operations within that instance. +2. **Module-Level Methods** (`writer.ai.ask`, `writer.ai.stream_ask`): Designed for querying multiple graphs simultaneously. These methods operate on a broader scale, allowing mixed inputs of graph objects and IDs. + + +• Use graph-level methods when working with a single graph instance. +• Use module-level methods when querying multiple graphs or when graph IDs are your primary input. + + +#### Parameters + +Both methods include: + • `question: str`: The main query for the LLM. + • *Optional* `subqueries: bool` (default: `False`): Allows the LLM to generate additional questions during response preparation for more detailed answers. Enabling this might increase response time. + +Method-level methods require: + • `graphs_or_graph_ids: list[Graph | str]`: A list of graphs to use for the question. You can pass `Graph` objects directly into the list, use graph IDs in string form, or a mix of both. + +#### Graph-level methods + +The graph-level methods, `Graph.ask` and `Graph.stream_ask`, are designed for interacting with a single graph. By calling these methods on a specific `Graph` instance, you can easily pose questions and retrieve answers tailored to that graph’s content. + + +```python ask +# Retrieve a specific graph +graph = retrieve_graph("f47ac10b-58cc-4372-a567-0e02b2c3d479") + +# Pose a question to the graph and get a complete response +response = graph.ask("What are the benefits of renewable energy?") +print(response) +``` +```python stream_ask +# Retrieve a specific graph +graph = retrieve_graph("f47ac10b-58cc-4372-a567-0e02b2c3d479") + +# Pose a question and stream the response in chunks +for chunk in graph.stream_ask("Explain the history of solar energy."): + print(chunk) +``` + + +#### Module-level methods +The module-level methods, `writer.ai.ask` and `writer.ai.stream_ask`, are designed for querying multiple graphs simultaneously. They are useful when you need to aggregate or compare data across multiple graphs. + + +```python ask +from writer.ai import ask + +# Pose a question to multiple graphs +response = ask( + question="What are the latest advancements in AI?", + graphs_or_graph_ids=[ + "550e8400-e29b-41d4-a716-446655440000", + "123e4567-e89b-12d3-a456-426614174000" + ] +) +print(response) +``` +```python stream_ask +from writer.ai import stream_ask + +# Stream responses from multiple graphs +for chunk in stream_ask( + question="Describe the key features of renewable energy sources.", + graphs_or_graph_ids=[ + "550e8400-e29b-41d4-a716-446655440000", + "123e4567-e89b-12d3-a456-426614174000" + ] +): + print(chunk) +``` + From 7fbb24898533570987549b1dd8db8638843e36cb Mon Sep 17 00:00:00 2001 From: Alexandre Rousseau Date: Tue, 26 Nov 2024 23:17:25 +0100 Subject: [PATCH 3/6] feat(ui): implement `CoreProgressBar` - WF-60 --- .../public/components/progressbar.png | Bin 0 -> 6831 bytes .../components/core/base/BaseInputWrapper.vue | 10 +- .../core/content/CoreProgressBar.vue | 188 ++++++++++++++++++ src/ui/src/composables/useFieldValue.ts | 10 + src/ui/src/composables/useFormatter.ts | 18 ++ src/ui/src/core/templateMap.ts | 2 + src/ui/src/renderer/syntheticEvents.ts | 5 +- 7 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 docs/framework/public/components/progressbar.png create mode 100644 src/ui/src/components/core/content/CoreProgressBar.vue create mode 100644 src/ui/src/composables/useFieldValue.ts create mode 100644 src/ui/src/composables/useFormatter.ts diff --git a/docs/framework/public/components/progressbar.png b/docs/framework/public/components/progressbar.png new file mode 100644 index 0000000000000000000000000000000000000000..8725283529506d0d09cc95dba2bb6730b1697c2e GIT binary patch literal 6831 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 JMYqkK|3C4EIHLdn literal 0 HcmV?d00001 diff --git a/src/ui/src/components/core/base/BaseInputWrapper.vue b/src/ui/src/components/core/base/BaseInputWrapper.vue index 1cb3b391b..ed3b64273 100644 --- a/src/ui/src/components/core/base/BaseInputWrapper.vue +++ b/src/ui/src/components/core/base/BaseInputWrapper.vue @@ -4,16 +4,16 @@ class="BaseInputWrapper" :class="{ horizontal: isHorizontal }" > - + diff --git a/src/ui/src/composables/useFieldValue.ts b/src/ui/src/composables/useFieldValue.ts new file mode 100644 index 000000000..595f1f93a --- /dev/null +++ b/src/ui/src/composables/useFieldValue.ts @@ -0,0 +1,10 @@ +import { ComputedRef, computed } from "vue"; + +type Fields = Record>; + +export function useFieldValueAsYesNo( + fields: Fields, + key: string, +): ComputedRef { + return computed(() => fields[key].value === "yes"); +} diff --git a/src/ui/src/composables/useFormatter.ts b/src/ui/src/composables/useFormatter.ts new file mode 100644 index 000000000..da47c5912 --- /dev/null +++ b/src/ui/src/composables/useFormatter.ts @@ -0,0 +1,18 @@ +import { computed, ComputedRef } from "vue"; + +export function usePercentageFormatter( + number: ComputedRef, + options: Pick< + Intl.NumberFormatOptions, + "minimumFractionDigits" | "maximumFractionDigits" + > = { + minimumFractionDigits: 0, + maximumFractionDigits: 1, + }, +) { + const formatter = new Intl.NumberFormat(undefined, { + style: "percent", + ...options, + }); + return computed(() => formatter.format(number.value)); +} diff --git a/src/ui/src/core/templateMap.ts b/src/ui/src/core/templateMap.ts index c3031ccbd..ec4b344f2 100644 --- a/src/ui/src/core/templateMap.ts +++ b/src/ui/src/core/templateMap.ts @@ -17,6 +17,7 @@ import CoreTags from "../components/core/content/CoreTags.vue"; import CoreAvatar from "../components/core/content/CoreAvatar.vue"; import CoreAnnotatedText from "../components/core/content/CoreAnnotatedText.vue"; import CoreJsonViewer from "../components/core/content/CoreJsonViewer.vue"; +import CoreProgressBar from "../components/core/content/CoreProgressBar.vue"; // input import CoreCheckboxInput from "../components/core/input/CoreCheckboxInput.vue"; @@ -136,6 +137,7 @@ const templateMap: TemplateMap = { jsonviewer: CoreJsonViewer, workflows_root: WorkflowsRoot, workflows_workflow: WorkflowsWorkflow, + progressbar: CoreProgressBar, }; const abstractTemplateMap: Record = {}; diff --git a/src/ui/src/renderer/syntheticEvents.ts b/src/ui/src/renderer/syntheticEvents.ts index df7bab08c..5fcf3b229 100644 --- a/src/ui/src/renderer/syntheticEvents.ts +++ b/src/ui/src/renderer/syntheticEvents.ts @@ -1,4 +1,7 @@ -export function getClick(ev: MouseEvent): CustomEvent { +/** + * @param ev event from a mouse click, or a keyboard click (using tab navigation, then click with `Enter`) + */ +export function getClick(ev: MouseEvent | KeyboardEvent): CustomEvent { const payload = { ctrlKey: ev.ctrlKey, shiftKey: ev.shiftKey, From f5e574eb09413b0b8b4c51842c26b585a637134a Mon Sep 17 00:00:00 2001 From: Alexandre Rousseau Date: Mon, 9 Dec 2024 09:15:07 +0100 Subject: [PATCH 4/6] fix(ui): add default value for Slider The default value for `useFormValueBroker` is an empty string. This make some incompatibilities with components that expect a different type of value and produces props warning. So I added a params `defaultValue` to `useFormValueBroker` to specifiy the default value when it's needed. --- src/ui/src/components/core/input/CoreSliderInput.vue | 1 + src/ui/src/components/core/input/CoreSliderRangeInput.vue | 3 ++- src/ui/src/renderer/useFormValueBroker.ts | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ui/src/components/core/input/CoreSliderInput.vue b/src/ui/src/components/core/input/CoreSliderInput.vue index b2d8b1971..4d2bf49e1 100644 --- a/src/ui/src/components/core/input/CoreSliderInput.vue +++ b/src/ui/src/components/core/input/CoreSliderInput.vue @@ -103,6 +103,7 @@ const { formValue, handleInput } = useFormValueBroker( wf, instancePath, rootInstance, + 50, ); diff --git a/src/ui/src/components/core/input/CoreSliderRangeInput.vue b/src/ui/src/components/core/input/CoreSliderRangeInput.vue index 3ebd4db60..332fe0588 100644 --- a/src/ui/src/components/core/input/CoreSliderRangeInput.vue +++ b/src/ui/src/components/core/input/CoreSliderRangeInput.vue @@ -6,7 +6,7 @@ > ( wf, instancePath, rootInstance, + [20, 50], ); diff --git a/src/ui/src/renderer/useFormValueBroker.ts b/src/ui/src/renderer/useFormValueBroker.ts index 90d8ce803..cc854b686 100644 --- a/src/ui/src/renderer/useFormValueBroker.ts +++ b/src/ui/src/renderer/useFormValueBroker.ts @@ -3,17 +3,19 @@ import { useEvaluator } from "@/renderer/useEvaluator"; import { Core, InstancePath } from "@/writerTypes"; /** - * * Encapsulates repeatable form value logic, including binding. * * @param wf * @param componentId + * @param defaultValue the initial value when binding is not set * @returns */ export function useFormValueBroker( wf: Core, instancePath: InstancePath, emitterEl: Ref, + // @ts-expect-error keep default string for compatibility reason + defaultValue: T = "", ) { const formValue: Ref = ref(); const isBusy = ref(false); @@ -110,7 +112,7 @@ export function useFormValueBroker( formValue, (newValue) => { if (typeof newValue === "undefined") { - formValue.value = "" as T; + formValue.value = defaultValue; } }, { immediate: true }, From 3bb930de82ebf5186196054fdebb187dad77058f Mon Sep 17 00:00:00 2001 From: Ramiro Medina <64783088+ramedina86@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:35:25 +0000 Subject: [PATCH 5/6] fix: Float coordinates --- src/ui/src/components/workflows/WorkflowsWorkflow.vue | 4 ++-- src/writer/core_ui.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ui/src/components/workflows/WorkflowsWorkflow.vue b/src/ui/src/components/workflows/WorkflowsWorkflow.vue index 6d2cb2166..8df276866 100644 --- a/src/ui/src/components/workflows/WorkflowsWorkflow.vue +++ b/src/ui/src/components/workflows/WorkflowsWorkflow.vue @@ -382,8 +382,8 @@ function moveNode(ev: MouseEvent) { const component = wf.getComponentById(nodeId); const { x, y } = getAdjustedCoordinates(ev); - const newX = x - offset.x; - const newY = y - offset.y; + const newX = Math.floor(x - offset.x); + const newY = Math.floor(y - offset.y); if (component.x == newX && component.y == newY) return; diff --git a/src/writer/core_ui.py b/src/writer/core_ui.py index a380a07a0..63d23e329 100644 --- a/src/writer/core_ui.py +++ b/src/writer/core_ui.py @@ -6,7 +6,7 @@ from enum import Enum from typing import Any, Dict, List, Literal, Optional, Union, cast -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator, validator from typing_extensions import TypedDict from writer.ss_types import ComponentDefinition, ServeMode @@ -57,6 +57,12 @@ class Component(BaseModel): x: Optional[int] = None y: Optional[int] = None + @field_validator("x", "y", mode="before") + def cast_float_to_int(cls, v): + if isinstance(v, float): + return int(v) + return v + def to_dict(self) -> Dict: """ Wrapper for model_dump to ensure backward compatibility. From 27b4e8a4ad9d7054dcb4dde63e3f7f71834d5e93 Mon Sep 17 00:00:00 2001 From: Ramiro Medina <64783088+ramedina86@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:35:44 +0000 Subject: [PATCH 6/6] chore: Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7ec18f681..ab7e2d602 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "writer" -version = "0.8.3rc1" +version = "0.8.3rc2" 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"