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();


// 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();


aiTask = GPAL.AI

.WithProvider(AIProviderType.Anthropic)

.WithCredentials(credentials)

.WithTask(AITask.Classification)

.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 the selected task's name followed by a colon (Classification:, Summarization:, and so on). outputArea is a GPALRichTextBox that renders the AI result as Markdown. statusBar is a GPALStatusStrip that starts at Ready, shows a processing message while the AI call is in flight, and then reports Response received or an error. 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

ICredentials 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 Sending request... and Waiting for response... while the AI call is in flight, then Response received, or an Error: message when the call fails. It does not go back to Ready. 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.Classification)

.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