mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET Workflows - Code Generation for Declarative Workflow (#655)
* Notes * Readme typo * Update readme * Checkpoint * Namespace fix * Fix ID and namespace * Checkpoint * Verified * Comments * Isolate "Kit" * Address note: static * Checkpoint * Checkpoint "Executor<>" * Prefix and internal executors * Test passing * Cleanup * Rename "session" concept * Revert workflow debug * Fix template base / pragma * Tune system scope * Update dotnet/src/Microsoft.Agents.Workflows.Declarative/CodeGen/ResetVariableTemplate.tt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix empty template * Add validation for codegen ut * Fix test * Codegen baselines * Constant * Prep * Mark TODO * Fix * Namespace * One more * Update baselines * Checkpoint * Checkpoint * Checkpoint * fme * Checkpoint * Another step * Fixed up * Roslyn * Fix * More cleaning * Async * Fix * Enum checkpoint * Refine enum * Checkpoint * Sync templates * Checkpoint * Streamline * Pre-merge analyzer updates * Foreach * Placeholders * Checkpoint * Clean-up * Sample path resolution * Checkpoint * Checkpoint - Workflow Code Building * Validation * Test cleanup * Update test basline * Update test baseline * Fix DefaultTemplate usage * Validation checkpoint * Fix break/continue edges * Verify generated code builds * Fix merge * Fix build validation * Update template handling of literal string values. * Test for metadata case * Update baselines * Fix merge * Checkpoint * Checkpoint: Conditions * Invoke Agent Checkpoint * Namespace * Address code-analysis issues * Cross platform test support * Invoke agent checkpoint * Clean sample * Checkpoint: Agent Invoke Input Messages * Checkpoint - Passing * Checkpoint * Regenerate all template + port conversation fix * Checkpoint: Tests good * Fix test for unbuntu * Fix build command * Checkpoint - E2E * Test fix * Update integration tests * Fix merge * Update * Checkpoint !!! * Baby steps * Checkpoint * Checkpoint E2E !!! * So close... * Integrate test validation * Fix merge * Rebase tests * Namespace * Namespace * Test cleanup * Sample comment cleanup * Checkpoint: List conversion * Include these * CheckPoint: ParseValue * Namespace * Fix sampel * More namspace * Comments * Test updates * Test fix * Better build * Shared code * Sort solution * Fix build * Prune solution * One more * Conversion matrix * Final table conversion --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx.Functions;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
+154
-2
@@ -1,14 +1,43 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class DataValueExtensions
|
||||
{
|
||||
public static DataValue ToDataValue(this object? value) =>
|
||||
value switch
|
||||
{
|
||||
null => DataValue.Blank(),
|
||||
UnassignedValue => DataValue.Blank(),
|
||||
FormulaValue formulaValue => formulaValue.ToDataValue(),
|
||||
DataValue dataValue => dataValue,
|
||||
bool booleanValue => BooleanDataValue.Create(booleanValue),
|
||||
int decimalValue => NumberDataValue.Create(decimalValue),
|
||||
long decimalValue => NumberDataValue.Create(decimalValue),
|
||||
float decimalValue => FloatDataValue.Create(decimalValue),
|
||||
decimal decimalValue => NumberDataValue.Create(decimalValue),
|
||||
double numberValue => FloatDataValue.Create(numberValue),
|
||||
string stringValue => StringDataValue.Create(stringValue),
|
||||
DateTime dateonlyValue when dateonlyValue.TimeOfDay == TimeSpan.Zero => DateDataValue.Create(dateonlyValue),
|
||||
DateTime datetimeValue => DateTimeDataValue.Create(datetimeValue),
|
||||
TimeSpan timeValue => TimeDataValue.Create(timeValue),
|
||||
object when value is IDictionary dictionaryValue => dictionaryValue.ToRecordValue(),
|
||||
object when value is IEnumerable tableValue => tableValue.ToTableValue(),
|
||||
_ => throw new DeclarativeModelException($"Unsupported variable type: {value.GetType().Name}"),
|
||||
};
|
||||
|
||||
public static FormulaValue ToFormula(this DataValue? value) =>
|
||||
value switch
|
||||
{
|
||||
@@ -65,12 +94,37 @@ internal static class DataValueExtensions
|
||||
DateTimeDataValue dateTimeValue => dateTimeValue.Value.DateTime,
|
||||
DateDataValue dateValue => dateValue.Value,
|
||||
TimeDataValue timeValue => timeValue.Value,
|
||||
TableDataValue tableValue => tableValue.Values.Select(value => value.ToDictionary()).ToArray(),
|
||||
RecordDataValue recordValue => recordValue.ToDictionary(),
|
||||
TableDataValue tableValue => tableValue.ToObject(),
|
||||
RecordDataValue recordValue => recordValue.ToObject(),
|
||||
OptionDataValue optionValue => optionValue.Value.Value,
|
||||
_ => throw new DeclarativeModelException($"Unsupported {nameof(DataValue)} type: {value.GetType().Name}"),
|
||||
};
|
||||
|
||||
public static Type ToClrType(this DataType type) =>
|
||||
type switch
|
||||
{
|
||||
BooleanDataType => typeof(bool),
|
||||
NumberDataType => typeof(decimal),
|
||||
FloatDataType => typeof(double),
|
||||
StringDataType => typeof(string),
|
||||
DateTimeDataType => typeof(DateTime),
|
||||
DateDataType => typeof(DateTime),
|
||||
TimeDataType => typeof(TimeSpan),
|
||||
TableDataType tableType => VariableType.ListType,
|
||||
RecordDataType recordValue => VariableType.RecordType,
|
||||
_ => throw new DeclarativeModelException($"Unsupported {nameof(DataValue)} type: {type.GetType().Name}"),
|
||||
};
|
||||
|
||||
public static IList<TElement>? AsList<TElement>(this DataValue? value)
|
||||
{
|
||||
if (value is null || value is BlankDataValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.ToObject().AsList<TElement>();
|
||||
}
|
||||
|
||||
public static FormulaValue NewBlank(this DataType? type) => FormulaValue.NewBlank(type?.ToFormulaType() ?? FormulaType.Blank);
|
||||
|
||||
public static RecordValue ToRecordValue(this RecordDataValue recordDataValue) =>
|
||||
@@ -88,6 +142,53 @@ internal static class DataValueExtensions
|
||||
return recordType;
|
||||
}
|
||||
|
||||
public static RecordDataValue ToRecordValue(this IDictionary value)
|
||||
{
|
||||
return DataValue.RecordFromFields(GetFields());
|
||||
|
||||
IEnumerable<KeyValuePair<string, DataValue>> GetFields()
|
||||
{
|
||||
yield return new KeyValuePair<string, DataValue>(TypeSchema.Discriminator, nameof(ExpandoObject).ToDataValue());
|
||||
|
||||
foreach (string key in value.Keys)
|
||||
{
|
||||
yield return new KeyValuePair<string, DataValue>(key, value[key].ToDataValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TableDataValue ToTableValue(this IEnumerable values)
|
||||
{
|
||||
IEnumerator enumerator = values.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return DataValue.EmptyTable;
|
||||
}
|
||||
|
||||
if (enumerator.Current is IDictionary)
|
||||
{
|
||||
DataValue.TableFromRecords(GetFields().ToImmutableArray());
|
||||
}
|
||||
|
||||
return DataValue.TableFromValues(GetValues().ToImmutableArray());
|
||||
|
||||
IEnumerable<RecordDataValue> GetFields()
|
||||
{
|
||||
foreach (IDictionary value in values)
|
||||
{
|
||||
yield return value.ToRecordValue();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<DataValue> GetValues()
|
||||
{
|
||||
foreach (object value in values)
|
||||
{
|
||||
yield return value.ToDataValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RecordType ParseRecordType(this RecordDataValue record)
|
||||
{
|
||||
RecordType recordType = RecordType.Empty();
|
||||
@@ -98,9 +199,60 @@ internal static class DataValueExtensions
|
||||
return recordType;
|
||||
}
|
||||
|
||||
private static object ToObject(this TableDataValue table)
|
||||
{
|
||||
DataValue? firstElement = table.Values.FirstOrDefault();
|
||||
if (firstElement is null)
|
||||
{
|
||||
return Array.Empty<object>();
|
||||
}
|
||||
|
||||
if (firstElement is RecordDataValue record)
|
||||
{
|
||||
if (record.Properties.Count == 1 && record.Properties.TryGetValue("Value", out DataValue? singleColumn))
|
||||
{
|
||||
record = singleColumn as RecordDataValue ?? record;
|
||||
}
|
||||
|
||||
if (record.Properties.TryGetValue(TypeSchema.Discriminator, out DataValue? value) && value is StringDataValue typeValue)
|
||||
{
|
||||
if (string.Equals(nameof(ChatMessage), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return table.ToChatMessages().ToArray();
|
||||
}
|
||||
|
||||
if (string.Equals(nameof(ExpandoObject), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return table.Values.Select(dataValue => dataValue.ToDictionary()).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return table.Values.Select(value => value.ToObject()).ToArray();
|
||||
}
|
||||
|
||||
private static object ToObject(this RecordDataValue record)
|
||||
{
|
||||
if (record.Properties.TryGetValue(TypeSchema.Discriminator, out DataValue? value) && value is StringDataValue typeValue)
|
||||
{
|
||||
if (string.Equals(nameof(ChatMessage), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return record.ToChatMessage();
|
||||
}
|
||||
|
||||
if (string.Equals(nameof(ExpandoObject), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return record.ToDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
return record.ToDictionary();
|
||||
}
|
||||
|
||||
private static Dictionary<string, object?> ToDictionary(this RecordDataValue record)
|
||||
{
|
||||
Dictionary<string, object?> result = [];
|
||||
result[TypeSchema.Discriminator] = nameof(ExpandoObject);
|
||||
foreach (KeyValuePair<string, DataValue> property in record.Properties)
|
||||
{
|
||||
result[property.Key] = property.Value.ToObject();
|
||||
|
||||
+1
-1
@@ -8,8 +8,8 @@ using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx.Functions;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class JsonDocumentExtensions
|
||||
{
|
||||
public static FrozenDictionary<string, object?> ParseRecord(this JsonDocument jsonDocument, VariableType recordType) => jsonDocument.RootElement.ParseRecord(recordType);
|
||||
|
||||
public static RecordValue ParseRecord(this JsonDocument jsonDocument, RecordDataType recordType) => jsonDocument.RootElement.ParseRecord(recordType);
|
||||
|
||||
private static FrozenDictionary<string, object?> ParseRecord(this JsonElement currentElement, VariableType recordType)
|
||||
{
|
||||
if (!recordType.IsRecord || recordType.Schema is null)
|
||||
{
|
||||
throw new DeclarativeActionException($"Unable to parse JSON element as {recordType.Type.Name}.");
|
||||
}
|
||||
|
||||
return ParseValues().ToFrozenDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
IEnumerable<KeyValuePair<string, object?>> ParseValues()
|
||||
{
|
||||
foreach (KeyValuePair<string, VariableType?> property in recordType.Schema)
|
||||
{
|
||||
JsonElement propertyElement = currentElement.GetProperty(property.Key);
|
||||
object? parsedValue =
|
||||
property.Value?.Type switch
|
||||
{
|
||||
null => null,
|
||||
_ when property.Value.Type == typeof(string) => propertyElement.GetString(),
|
||||
_ when property.Value.Type == typeof(int) => propertyElement.GetInt32(),
|
||||
_ when property.Value.Type == typeof(long) => propertyElement.GetInt64(),
|
||||
_ when property.Value.Type == typeof(decimal) => propertyElement.GetDecimal(),
|
||||
_ when property.Value.Type == typeof(double) => propertyElement.GetDouble(),
|
||||
_ when property.Value.Type == typeof(bool) => propertyElement.GetBoolean(),
|
||||
_ when property.Value.Type == typeof(DateTime) => propertyElement.GetDateTime(),
|
||||
_ when property.Value.Type == typeof(TimeSpan) => propertyElement.GetDateTimeOffset().TimeOfDay,
|
||||
_ when property.Value.IsRecord => propertyElement.ParseRecord(property.Value),
|
||||
//TableDataType tableType => ParseTable(tableType, propertyElement),
|
||||
_ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"),
|
||||
};
|
||||
yield return new KeyValuePair<string, object?>(property.Key, parsedValue);
|
||||
}
|
||||
|
||||
//static TableValue ParseTable(TableDataType tableType, JsonElement propertyElement)
|
||||
//{
|
||||
// RecordDataType recordType = tableType.ToRecord();
|
||||
// return
|
||||
// FormulaValue.NewTable(
|
||||
// recordType.ToRecordType(),
|
||||
// propertyElement.EnumerateArray().Select(tableElement => tableElement.ParseRecord(recordType)));
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private static RecordValue ParseRecord(this JsonElement currentElement, RecordDataType recordType)
|
||||
{
|
||||
return FormulaValue.NewRecordFromFields(ParseValues());
|
||||
|
||||
IEnumerable<NamedValue> ParseValues()
|
||||
{
|
||||
foreach (KeyValuePair<string, PropertyInfo> property in recordType.Properties)
|
||||
{
|
||||
JsonElement propertyElement = currentElement.GetProperty(property.Key);
|
||||
FormulaValue? parsedValue =
|
||||
property.Value.Type switch
|
||||
{
|
||||
StringDataType => FormulaValue.New(propertyElement.GetString()),
|
||||
NumberDataType => FormulaValue.New(propertyElement.GetDecimal()),
|
||||
BooleanDataType => FormulaValue.New(propertyElement.GetBoolean()),
|
||||
DateTimeDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
DateDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
TimeDataType => FormulaValue.New(propertyElement.GetDateTimeOffset().TimeOfDay),
|
||||
RecordDataType recordType => propertyElement.ParseRecord(recordType),
|
||||
TableDataType tableType => ParseTable(tableType, propertyElement),
|
||||
_ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"),
|
||||
};
|
||||
yield return new NamedValue(property.Key, parsedValue);
|
||||
}
|
||||
|
||||
static TableValue ParseTable(TableDataType tableType, JsonElement propertyElement)
|
||||
{
|
||||
RecordDataType recordType = tableType.ToRecord();
|
||||
return
|
||||
FormulaValue.NewTable(
|
||||
recordType.ToRecordType(),
|
||||
propertyElement.EnumerateArray().Select(tableElement => tableElement.ParseRecord(recordType)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class ObjectExtensions
|
||||
{
|
||||
public static IList<TElement>? AsList<TElement>(this object? value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
null => null,
|
||||
UnassignedValue => null,
|
||||
BlankValue => null,
|
||||
BlankDataValue => null,
|
||||
IList<TElement> list => list,
|
||||
IEnumerable<TElement> enumerable => enumerable.ToList(),
|
||||
TElement element => [element],
|
||||
_ => TypedElements().ToList(),
|
||||
};
|
||||
|
||||
IEnumerable<TElement> TypedElements()
|
||||
{
|
||||
if (value is not IEnumerable enumerable)
|
||||
{
|
||||
throw new DeclarativeActionException($"Value '{value.GetType().Name}' is not '{nameof(IEnumerable)}'.");
|
||||
}
|
||||
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
if (item is not TElement element)
|
||||
{
|
||||
throw new DeclarativeActionException($"Item '{item.GetType().Name}' is not of type '{typeof(TElement).Name}'");
|
||||
}
|
||||
|
||||
yield return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static object? ConvertType(this object? sourceValue, VariableType targetType)
|
||||
{
|
||||
if (!targetType.IsValid())
|
||||
{
|
||||
throw new DeclarativeActionException($"Unsupported type: '{targetType.Type.Name}'.");
|
||||
}
|
||||
|
||||
if (sourceValue != null && targetType.Type.IsAssignableFrom(sourceValue.GetType()))
|
||||
{
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
return targetType switch
|
||||
{
|
||||
_ when typeof(string).IsAssignableFrom(targetType.Type) => ConvertToString(),
|
||||
_ when typeof(bool).IsAssignableFrom(targetType.Type) => ConvertToBool(),
|
||||
_ when targetType.IsRecord => ConvertToRecord(),
|
||||
_ when targetType.IsList => ConvertToList(),
|
||||
_ when typeof(int).IsAssignableFrom(targetType.Type) => ConvertToInt(),
|
||||
_ when typeof(long).IsAssignableFrom(targetType.Type) => ConvertToLong(),
|
||||
_ when typeof(decimal).IsAssignableFrom(targetType.Type) => ConvertToDecimal(),
|
||||
_ when typeof(double).IsAssignableFrom(targetType.Type) => ConvertToDouble(),
|
||||
_ when typeof(DateTime).IsAssignableFrom(targetType.Type) => ConvertToDateTime(),
|
||||
_ when typeof(TimeSpan).IsAssignableFrom(targetType.Type) => ConvertToTimeSpan(),
|
||||
_ => throw new DeclarativeActionException($"Unsupported type: '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
bool? ConvertToBool() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => bool.Parse(s),
|
||||
int i => i != 0,
|
||||
long l => l != 0,
|
||||
decimal c => c != 0,
|
||||
double d => d != 0,
|
||||
DateTime dt => dt > DateTime.MinValue,
|
||||
TimeSpan ts => ts > TimeSpan.MinValue,
|
||||
_ => sourceValue != null,
|
||||
};
|
||||
|
||||
int? ConvertToInt() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => int.Parse(s),
|
||||
int i => i,
|
||||
long l => Convert.ToInt32(l),
|
||||
decimal c => Convert.ToInt32(c),
|
||||
double d => Convert.ToInt32(d),
|
||||
DateTime dt => Convert.ToInt32(dt),
|
||||
TimeSpan ts => Convert.ToInt32(ts),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
long? ConvertToLong() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => long.Parse(s),
|
||||
int i => i,
|
||||
long l => l,
|
||||
decimal c => Convert.ToInt64(c),
|
||||
double d => Convert.ToInt64(d),
|
||||
DateTime dt => Convert.ToInt64(dt),
|
||||
TimeSpan ts => Convert.ToInt64(ts),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
decimal? ConvertToDecimal() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => decimal.Parse(s),
|
||||
int i => i,
|
||||
long l => l,
|
||||
decimal c => c,
|
||||
double d => Convert.ToDecimal(d),
|
||||
DateTime dt => Convert.ToDecimal(dt),
|
||||
TimeSpan ts => Convert.ToDecimal(ts),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
double? ConvertToDouble() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => double.Parse(s),
|
||||
int i => i,
|
||||
long l => l,
|
||||
decimal c => Convert.ToDouble(c),
|
||||
double d => d,
|
||||
DateTime dt => dt.Ticks,
|
||||
TimeSpan ts => ts.Ticks,
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
DateTime? ConvertToDateTime() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => DateTime.Parse(s),
|
||||
int i => new DateTime(i),
|
||||
long l => new DateTime(l),
|
||||
decimal c => new DateTime(Convert.ToInt64(c)),
|
||||
double d => new DateTime(Convert.ToInt64(d)),
|
||||
DateTime dt => dt,
|
||||
TimeSpan ts => DateTime.Now.Date.AddTicks(ts.Ticks),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
TimeSpan? ConvertToTimeSpan() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => TimeSpan.Parse(s),
|
||||
int i => TimeSpan.FromTicks(i),
|
||||
long l => TimeSpan.FromTicks(l),
|
||||
decimal c => TimeSpan.FromTicks(Convert.ToInt64(c)),
|
||||
double d => TimeSpan.FromTicks(Convert.ToInt64(d)),
|
||||
DateTime dt => dt.TimeOfDay,
|
||||
TimeSpan ts => ts,
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
object? ConvertToList() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
//string jsonText => JsonDocument.Parse(jsonText.TrimJsonDelimiter()).ParseRecord(targetType),
|
||||
_ => throw new DeclarativeActionException($"Cannot convert '{sourceValue?.GetType().Name}' to 'Record' (expected JSON string)."),
|
||||
};
|
||||
|
||||
object? ConvertToRecord() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string jsonText => JsonDocument.Parse(jsonText.TrimJsonDelimiter()).ParseRecord(targetType),
|
||||
_ => throw new DeclarativeActionException($"Cannot convert '{sourceValue?.GetType().Name}' to 'Record' (expected JSON string)."),
|
||||
};
|
||||
|
||||
string? ConvertToString() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string sourceText => sourceText,
|
||||
DateTime dateTime => dateTime.ToString("o"), // ISO 8601
|
||||
TimeSpan timeSpan => timeSpan.ToString("c"), // Constant ("c") format
|
||||
_ => $"{sourceValue}",
|
||||
};
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class RecordDataTypeExtensions
|
||||
{
|
||||
public static RecordValue ParseRecord(this RecordDataType recordType, JsonElement currentElement)
|
||||
{
|
||||
return FormulaValue.NewRecordFromFields(ParseValues());
|
||||
|
||||
IEnumerable<NamedValue> ParseValues()
|
||||
{
|
||||
foreach (KeyValuePair<string, PropertyInfo> property in recordType.Properties)
|
||||
{
|
||||
JsonElement propertyElement = currentElement.GetProperty(property.Key);
|
||||
FormulaValue? parsedValue =
|
||||
property.Value.Type switch
|
||||
{
|
||||
StringDataType => FormulaValue.New(propertyElement.GetString()),
|
||||
NumberDataType => FormulaValue.New(propertyElement.GetDecimal()),
|
||||
BooleanDataType => FormulaValue.New(propertyElement.GetBoolean()),
|
||||
DateTimeDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
DateDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
TimeDataType => FormulaValue.New(propertyElement.GetDateTimeOffset().TimeOfDay),
|
||||
RecordDataType recordType => recordType.ParseRecord(propertyElement),
|
||||
TableDataType tableType => ParseTable(tableType, propertyElement),
|
||||
_ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"),
|
||||
};
|
||||
yield return new NamedValue(property.Key, parsedValue);
|
||||
}
|
||||
|
||||
static TableValue ParseTable(TableDataType tableType, JsonElement propertyElement)
|
||||
{
|
||||
RecordDataType recordType = tableType.ToRecord();
|
||||
return
|
||||
FormulaValue.NewTable(
|
||||
recordType.ToRecordType(),
|
||||
propertyElement.EnumerateArray().Select(tableElement => ParseRecord(recordType, tableElement)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
@@ -27,4 +29,30 @@ internal static partial class StringExtensions
|
||||
|
||||
public static FormulaValue ToFormula(this string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? FormulaValue.NewBlank() : FormulaValue.New(value);
|
||||
|
||||
public static string FormatType(this string identifier) => FormatIdentifier(identifier);
|
||||
|
||||
public static string FormatName(this string identifier) => FormatIdentifier(identifier, skipFirst: true);
|
||||
|
||||
private static string FormatIdentifier(string identifier, bool skipFirst = false)
|
||||
{
|
||||
string[] words = identifier.Split('_');
|
||||
|
||||
// Capitalize each word
|
||||
for (int index = skipFirst ? 1 : 0; index < words.Length; ++index)
|
||||
{
|
||||
words[index] = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(words[index]);
|
||||
}
|
||||
|
||||
// Combine the words and return
|
||||
return string.Concat(words);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ByLine(this string source)
|
||||
{
|
||||
foreach (string line in source.Trim().Split('\n'))
|
||||
{
|
||||
yield return line.TrimEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user