Integrations

Building a Live AI Summarizer Form

Complete Program

The full LiveAISummarizer sample project (in the GPAL.sln solution), start to finish. Each part is explained below.

using System;

using System.Drawing;

using GenerallyPositive;

using GenerallyPositive.GPALForm;

using static GenerallyPositive.Enums;

using static GenerallyPositive.GPALForm.GPALForm;


namespace LiveAISummarizer

{

class Program

{

static IGPALAI aiTask; // set in Main; combo callback checks for null


static readonly Array taskValues = Enum.GetValues(typeof(AITask));


static GPALLabel instructions = GPAL.Label

.WithText("Type or paste text below. The output updates automatically as you type.")

.WithFont(new Font("Segoe UI", 10.0f))

.ToGPALObject();


static GPALTextArea inputArea = GPAL.TextArea

.WithName("input")

.WithText("")

.WithHeight(200)

.ToGPALObject();


static GPALLabel taskLabel = GPAL.Label

.WithText("AI Task:")

.WithFont(new Font("Segoe UI", 10.0f))

.ToGPALObject();


static GPALComboBox taskCombo = (GPALComboBox)GPAL.ComboBox

.WithItems(taskValues)

.WithSelectedIndex(0)

.WithName("taskSelector")

.WithCallback<EventHandler>((s, e) =>

{

if (aiTask == null) return;

int idx = taskCombo.SelectedIndex;

if (idx < 0 || idx >= taskValues.Length) return;

((IAllowAITaskConfig)aiTask).WithTask((AITask)taskValues.GetValue(idx));

outputLabel.Text = taskCombo.SelectedValue + ":";

if (!string.IsNullOrWhiteSpace(inputArea.Text))

aiTask.Execute();

})

.WithToolTip("Select the AI task to run against your input")

.ToGPALObject();


static GPALLabel outputLabel = GPAL.Label

.WithText(taskValues.GetValue(0) + ":")

.WithFont(new Font("Segoe UI", 10.0f))

.ToGPALObject();


static GPALRichTextBox outputArea = (GPALRichTextBox)GPAL.RichTextBox

.WithName("output")

.WithText("")

.WithHeight(120)

.ToGPALObject();


static GPALStatusStrip statusBar = (GPALStatusStrip)GPAL.StatusStrip

.WithName("status")

.WithText("Ready")

.ToGPALObject();


[STAThread]

static void Main(string[] args)

{

GPAL.WithPublishToConsole(GPALEventType.All & ~GPALEventType.DEEPDEBUG);


ICredentials credentials = GPAL.CredentialsFor(CredentialServiceType.StaticKey)

.WithKeyFromEnv("ANTKEY")

.ToGPALObject();


IRESTClient restClient = GPAL.RESTClient

.WithAPIBase("http://localhost:3000/")

.ToGPALObject();


credentials = GPAL.CredentialsFor(CredentialServiceType.StaticKey)

.WithKeyFromApi(restClient)

.WithEndpoint("ANTKEY")

.ToGPALObject();


aiTask = GPAL.AI

.WithProvider(AIProviderType.Anthropic)

.WithCredentials(credentials)

.WithTask(AITask.Summarization)

.WithLiveInputFrom(inputArea, debounceMilliseconds: 800)

.WithOutputTo(outputArea)

.WithStatusTo(statusBar)

.ToGPALObject();


GPALForm form = GPAL.Form

.WithTitle("Live AI Summarizer")

.WithLeft(400)

.WithTop(200)

.WithWidth(540)

.WithHeight(540)

.WithFormControl(instructions)

.WithFormControl(inputArea)

.WithFormControl(taskLabel)

.WithFormControl(taskCombo)

.WithFormControl(outputLabel)

.WithFormControl(outputArea)

.WithFormControl(statusBar)

.ToGPALObject();


form.ShowDialog();

}

}

}

The Form Controls

Seven controls make up the form. inputArea is a GPALTextArea where text gets typed or pasted. taskLabel and taskCombo sit between the input and the output - the combo lets the user pick which AI task to run. outputLabel changes its text to match the selected task (Summary:, Generated Text:, etc.). outputArea is a GPALRichTextBox that renders the AI result as Markdown. statusBar is a GPALStatusStrip that shows Ready when idle and a processing message while the AI call is in flight. aiTask is declared at class level (not inside Main) so the combo callback can reach it.

static IGPALAI aiTask; // class-level so the combo callback can swap the task


static readonly Array taskValues = Enum.GetValues(typeof(AITask));


static GPALLabel instructions = GPAL.Label

.WithText("Type or paste text below. The output updates automatically as you type.")

.WithFont(new Font("Segoe UI", 10.0f))

.ToGPALObject();


static GPALTextArea inputArea = GPAL.TextArea

.WithName("input")

.WithText("")

.WithHeight(200)

.ToGPALObject();


static GPALLabel taskLabel = GPAL.Label

.WithText("AI Task:")

.WithFont(new Font("Segoe UI", 10.0f))

.ToGPALObject();


// taskCombo - see The Task Selector Combo below


static GPALLabel outputLabel = GPAL.Label

.WithText(taskValues.GetValue(0) + ":")

.WithFont(new Font("Segoe UI", 10.0f))

.ToGPALObject();


static GPALRichTextBox outputArea = (GPALRichTextBox)GPAL.RichTextBox

.WithName("output")

.WithText("")

.WithHeight(120)

.ToGPALObject();


static GPALStatusStrip statusBar = (GPALStatusStrip)GPAL.StatusStrip

.WithName("status")

.WithText("Ready")

.ToGPALObject();

The Task Selector Combo

taskCombo is populated directly from the AITask enum via Enum.GetValues - no hardcoded string list. WithItems(Array) (the overload that accepts a non-generic Array) calls ToString() on each enum value and adds the result as a combo item. The callback reads the selected index, fetches the corresponding enum value via taskValues.GetValue(idx) and casts it to AITask, calls WithTask to hot-swap the active task, updates outputLabel using taskCombo.SelectedValue (the same string WithItems stored), and re-runs if there is already text in inputArea. The null guard on aiTask protects against the callback firing before Main finishes wiring the AI chain.

static GPALComboBox taskCombo = (GPALComboBox)GPAL.ComboBox

.WithItems(taskValues)

.WithSelectedIndex(0)

.WithName("taskSelector")

.WithCallback<EventHandler>((s, e) =>

{

if (aiTask == null) return;

int idx = taskCombo.SelectedIndex;

if (idx < 0 || idx >= taskValues.Length) return;

((IAllowAITaskConfig)aiTask).WithTask((AITask)taskValues.GetValue(idx));

outputLabel.Text = taskCombo.SelectedValue + ":";

if (!string.IsNullOrWhiteSpace(inputArea.Text))

aiTask.Execute();

})

.WithToolTip("Select the AI task to run against your input")

.ToGPALObject();

NOTE

WithTask on an existing IGPALAI replaces the active task without rebuilding the whole AI chain. The debounce timer and live-input wiring stay intact - only the task changes. This is why aiTask must be at class level rather than a local variable in Main.

Credentials and the API Key

The Anthropic API key is read from the ANTKEY environment variable via WithKeyFromEnv. The code also shows an alternative: fetching the key live from a REST API endpoint, which keeps it out of source control and allows rotation without redeploying. That path is left in as commented-out reference - swap it in if your deployment serves keys through an API rather than environment variables.

// Active: read the key from the ANTKEY environment variable

credentials = GPAL.CredentialsFor(CredentialServiceType.StaticKey)

.WithKeyFromEnv("ANTKEY")

.ToGPALObject();


// Alternative: fetch the key live from a REST API endpoint

//IRESTClient restClient = GPAL.RESTClient

// .WithAPIBase("http://localhost:3000/")

// .ToGPALObject();

//credentials = GPAL.CredentialsFor(CredentialServiceType.StaticKey)

// .WithKeyFromApi(restClient)

// .WithEndpoint("ANTKEY")

// .ToGPALObject();

Wiring the AI Task with WithLiveInputFrom

WithLiveInputFrom(inputArea, debounceMilliseconds: 800) registers a debounced TextChanged callback on inputArea that calls Execute() automatically after 800ms of no typing. WithOutputTo(outputArea) writes each result back to outputArea as Markdown. WithStatusTo(statusBar) updates the status strip with a processing message while the AI call is in flight and resets it to Ready when done. The whole chain assigns into the class-level aiTask field so the combo callback can call WithTask on it later.

aiTask = GPAL.AI

.WithProvider(AIProviderType.Anthropic)

.WithCredentials(credentials)

.WithTask(AITask.Summarization)

.WithLiveInputFrom(inputArea, debounceMilliseconds: 800)

.WithOutputTo(outputArea)

.WithStatusTo(statusBar)

.ToGPALObject();

NOTE

See Building Live AI Forms with WithLiveInputFrom for why WithLiveInputFrom has to be called before WithFormControl/Show/ShowDialog.

Building and Showing the Form

With the AI task wired and the combo ready, the form is built by adding all seven controls in top-to-bottom order via WithFormControl. The height is 540 to accommodate the extra taskLabel and taskCombo row. form.ShowDialog() displays the form and blocks until it is closed - from that point on, typing in inputArea triggers the debounced summarizer, and changing the combo hot-swaps the task.

GPALForm form = GPAL.Form

.WithTitle("Live AI Summarizer")

.WithLeft(400)

.WithTop(200)

.WithWidth(540)

.WithHeight(540)

.WithFormControl(instructions)

.WithFormControl(inputArea)

.WithFormControl(taskLabel)

.WithFormControl(taskCombo)

.WithFormControl(outputLabel)

.WithFormControl(outputArea)

.WithFormControl(statusBar)

.ToGPALObject();


form.ShowDialog();

💬 Ask GPAL