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
23 changes: 10 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,25 @@ TypeChat.Program translates natural language requests into simple programs (***P
);

## TypeChat.SemanticKernel ##
TypeChat.SemanticKernel makes it easy to get ***strongly typed*** .NET objects from the [Microsoft Semantic Kernel](https://github.com/microsoft/semantic-kernel).

With auto-generation of Typescript schema:

// Translates user input into a typed responses classifying sentiment
var translator = new JsonTranslator<SentimentResponse>(
new LanguageModel(Config.LoadOpenAI()),
new TypeValidator<SentimentResponse>()
);

TypeChat.SemanticKernel also contains experimental support to dynamically synthesize and execute **programs** that call Semantic Kernel plugins.

The library contains classes for:
* LLM bindings for TypeChat using the Semantic Kernel. All TypeChat examples use the Semantic Kernel to call LLMs
* Program synthesis with Plugins: Automatically turns registered plugins into a PluginAPI that programs synthesized by the LLM can call. [Plugins Example](examples/Plugins/Program.cs)

* **Program synthesis with Plugins**: Automatically turns registered plugins into a PluginAPI that programs synthesized by the LLM can call. [Plugins Example](examples/Plugins/Program.cs)
# TypeChat.Dialog
TypeChat.Dialog is an early version of framework desiged for strongly typed interactions with Agents with built in interaction history and other features.

// Create an agent that interactively helps the user enter their health information, such as medications and conditions
new Agent<HealthDataResponse>(new LanguageModel(Config.LoadOpenAI()))

# TypeChat.App
Helper classes for:
* Console Apps
* Text Classification
* Intent dispatch and routing
* Generation and validation of programs that use Plugins



# Getting Started
## Building
Expand Down
25 changes: 8 additions & 17 deletions examples/Plugins/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,26 @@ public class PluginApp : ConsoleApp
{
OpenAIConfig _config;
IKernel _kernel;
ProgramTranslator _translator;
PluginApi _pluginApi;
SchemaText _pluginSchema;
PluginProgramTranslator _programTranslator;
ProgramInterpreter _interpreter;

public PluginApp()
{
InitPlugins();
_translator = new ProgramTranslator(
_kernel.LanguageModel(_config.Model),
new ProgramValidator(new PluginProgramValidator(_pluginApi.TypeInfo)),
_pluginSchema
);
_translator.MaxRepairAttempts = 2;
_programTranslator = new PluginProgramTranslator(_kernel, _config.Model);
_programTranslator.Translator.MaxRepairAttempts = 2;
_interpreter = new ProgramInterpreter();
// Uncomment to see ALL raw messages to and from the AI
//base.SubscribeAllEvents(_translator);
//base.SubscribeAllEvents(_translator.Translator);
}

public IKernel Kernel => _kernel;
public string Schema => _pluginSchema;
public string Schema => _programTranslator.Schema;

public override async Task ProcessRequestAsync(string input, CancellationToken cancelToken)
{
using Program program = await _translator.TranslateAsync(input, cancelToken);
program.Print(_pluginApi.TypeName);
using Program program = await _programTranslator.TranslateAsync(input, cancelToken);
program.Print(_programTranslator.Api.TypeName);
Console.WriteLine();

if (program.IsComplete)
Expand All @@ -50,7 +44,7 @@ async Task RunProgram(Program program)
return;
}
Console.WriteLine("Running program");
string result = await _interpreter.RunAsync(program, _pluginApi.InvokeAsync);
string result = await _interpreter.RunAsync(program, _programTranslator.Api.InvokeAsync);
if (!string.IsNullOrEmpty(result))
{
Console.WriteLine(result);
Expand All @@ -65,9 +59,6 @@ void InitPlugins()
_kernel.ImportSkill(new FoldersPlugin());
_kernel.ImportSkill(new StringPlugin());
_kernel.ImportSkill(new TimePlugin());

_pluginApi = new PluginApi(_kernel);
_pluginSchema = _pluginApi.TypeInfo.ExportSchema(_pluginApi.TypeName);
}

public static async Task<int> Main(string[] args)
Expand Down
5 changes: 1 addition & 4 deletions examples/Sentiment/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ public class SentimentApp : ConsoleApp

public SentimentApp()
{
_translator = new JsonTranslator<SentimentResponse>(
new LanguageModel(Config.LoadOpenAI()),
new TypeValidator<SentimentResponse>()
);
_translator = new JsonTranslator<SentimentResponse>(new LanguageModel(Config.LoadOpenAI()));
}

public override async Task ProcessRequestAsync(string input, CancellationToken cancelToken)
Expand Down
1 change: 1 addition & 0 deletions src/typechat.app/Includes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
global using System.CommandLine.Parsing;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Configuration.Json;
global using Microsoft.SemanticKernel;
global using Microsoft.TypeChat;
32 changes: 32 additions & 0 deletions src/typechat.app/PluginProgramTranslator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.TypeChat;

public class PluginProgramTranslator
{
IKernel _kernel;
ProgramTranslator _translator;
PluginApi _pluginApi;
SchemaText _pluginSchema;

public PluginProgramTranslator(IKernel kernel, ModelInfo model)
{
_kernel = kernel;
_pluginApi = new PluginApi(_kernel);
_pluginSchema = _pluginApi.TypeInfo.ExportSchema(_pluginApi.TypeName);
_translator = new ProgramTranslator(
_kernel.LanguageModel(model),
new ProgramValidator(new PluginProgramValidator(_pluginApi.TypeInfo)),
_pluginSchema
);
}

public ProgramTranslator Translator => _translator;
public PluginApi Api => _pluginApi;
public SchemaText Schema => _pluginSchema;

public Task<Program> TranslateAsync(string input, CancellationToken cancelToken)
{
return _translator.TranslateAsync(input, cancelToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.TypeChat;
using Microsoft.TypeChat.Schema;

namespace Plugins;
namespace Microsoft.TypeChat;

public class PluginProgramValidator : ProgramVisitor, IProgramValidator
{
Expand All @@ -28,7 +28,7 @@ public Result<Program> ValidateProgram(Program program)
}
}

void CSharpValidate(Program program)
public void CSharpValidate(Program program)
{
using StringWriter sw = new StringWriter();
new ProgramWriter(sw).Write(program, "IPlugin");
Expand Down
78 changes: 61 additions & 17 deletions src/typechat/Schema/TypeExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,36 @@

namespace Microsoft.TypeChat.Schema;

/// <summary>
/// Used by classes that exports Type information of type T
/// </summary>
/// <typeparam name="T">The type of the type information being exported</typeparam>
public abstract class TypeExporter<T>
{
HashSet<T> _exportedTypes;
Queue<T>? _pendingTypes;

/// <summary>
/// Create an exporter
/// </summary>
public TypeExporter()
{
_exportedTypes = new HashSet<T>();
}

/// <summary>
/// Reset internal state, allowing this exporter to be reused
/// </summary>
public virtual void Clear()
{
_exportedTypes?.Clear();
_pendingTypes?.Clear();
}

/// <summary>
/// Add a type to the pending list of types needing export
/// </summary>
/// <param name="type"></param>
public void AddPending(T type)
{
if (!IsExported(type) && ShouldExport(type))
Expand All @@ -29,38 +43,36 @@ public void AddPending(T type)
}
}

/// <summary>
/// Add multiple types to the pending types needing export
/// </summary>
/// <param name="types"></param>
public void AddPending(IEnumerable<T> types)
{
ArgumentNullException.ThrowIfNull(types, nameof(types));
foreach (var t in types)
{
AddPending(t);
}
}

public T? GetPending()
{
if (_pendingTypes != null && _pendingTypes.Count > 0)
{
return _pendingTypes.Dequeue();
}

return default;
}

/// <summary>
/// Has the given type been exported?
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public bool IsExported(T type) => _exportedTypes.Contains(type);

public void Export(T type)
{
AddPending(type);
ExportQueued();
}

protected void AddExported(T type)
{
_exportedTypes.Add(type);
ExportPending();
}

public virtual void ExportQueued()
/// <summary>
/// Export all currently pending Types
/// </summary>
public virtual void ExportPending()
{
T? type;
while ((type = GetPending()) != null)
Expand All @@ -69,7 +81,39 @@ public virtual void ExportQueued()
}
}

/// <summary>
/// Implemented by exporters
/// After exporting a type, exporters should call AddExported(...)
/// </summary>
/// <param name="t"></param>
public abstract void ExportType(T t);

/// <summary>
/// Should a type be exported? E.g primitive types may not be.
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected virtual bool ShouldExport(T t) => true;
/// <summary>
/// Update the exported list...
/// </summary>
/// <param name="type"></param>
protected void AddExported(T type)
{
_exportedTypes.Add(type);
}

/// <summary>
/// Dequeue the next pendin type for export
/// </summary>
/// <returns></returns>
T? GetPending()
{
if (_pendingTypes != null && _pendingTypes.Count > 0)
{
return _pendingTypes.Dequeue();
}

return default;
}
}
Loading