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
154 changes: 154 additions & 0 deletions blog/2026-07-27-v0-0-76.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
slug: release-v0-0-76
title: Release v0.0.76
authors: [vnext-team]
tags: [release, duyuru, tasks, caching, ai]
date: 2026-07-27
---

## Overview

This release adds AI to the task palette and sharpens the caching and concurrency primitives. A new **Dapr Conversation task** (`TaskType = 20`) invokes an LLM/AI provider (OpenAI, Anthropic, Bedrock, …) provider-agnostically through the Dapr Conversation building block ([#844](https://github.com/burgan-tech/vnext/issues/844)). **Function result caching gains vary-by headers** — `varyByHeaders` / `varyByHeaderPrefixes` feed a `varyKey(context)` key-expression helper so one function keeps separate cache variants per header value ([#839](https://github.com/burgan-tech/vnext/issues/839)). Flow-level cache tuning is **consolidated under a single `attributes.config` object** (`config.functionCache.ttlSeconds`), the author-controlled TTL for the built-in instance functions ([#846](https://github.com/burgan-tech/vnext/issues/846)). **Distributed resource locks** become production-ready with **idempotent release and automatic terminal cleanup** ([#840](https://github.com/burgan-tech/vnext/issues/840)), plus a per-subInstance lock key for subflow terminal-outcome propagation ([#845](https://github.com/burgan-tech/vnext/issues/845)). **Wizard states are now treated like normal states** in the State/View functions ([#838](https://github.com/burgan-tech/vnext/issues/838)). The release also fixes author `Content-Type` preservation ([#847](https://github.com/burgan-tech/vnext/issues/847)), case-insensitive request-header normalization ([#841](https://github.com/burgan-tech/vnext/issues/841)), and init mappings support for publish ([#848](https://github.com/burgan-tech/vnext/issues/848)). This release runs on component **schema 0.0.51**.

{/* truncate */}

---

## Features

### Dapr Conversation task — provider-agnostic LLM calls (#844)

A new task type, **DaprConversation** (`TaskType = 20`), invokes an LLM/AI provider through Dapr's Conversation building block — the same sidecar the other Dapr task types use. The provider and its credentials live in a **domain-owned** Dapr `conversation` component (e.g. `openai`); the task only references the component name and the messages.

```json
{
"attributes": {
"type": "20",
"config": {
"componentName": "openai",
"inputs": [
{ "role": "system", "content": "You summarize customer complaints." },
{ "role": "user", "content": "Summarize the complaint in two sentences." }
],
"parameters": { "model": "gpt-4o-mini", "maxTokens": "512" },
"temperature": 0.2,
"scrubPII": true
}
}
}
```

- Required config is just `componentName`; `inputs` is an array of `role`/`content` messages, and `parameters` carries provider-specific string values (`model`, `maxTokens`, …).
- Optional `metadata`, `contextId` (stateful conversations), `temperature`, `scrubPII`, and `timeoutSeconds` (default 30).
- The runtime component is installed via the helm chart ([vnext-helm-charts #28](https://github.com/burgan-tech/vnext-helm-charts/pull/28)), so shipping a new provider needs no runtime redeploy.

> **Reference:** issue [#844](https://github.com/burgan-tech/vnext/issues/844), helm PR [vnext-helm-charts #28](https://github.com/burgan-tech/vnext-helm-charts/pull/28) — see also the new [Dapr Conversation Task](/docs/components/tasks/dapr-conversation) component page.

### Function cache vary-by headers (#839)

The function `attributes.cache` block gains two fields — **`varyByHeaders`** (exact request-header names) and **`varyByHeaderPrefixes`** (header-name prefixes) — that fold the named headers into the cache key. They form the header-name set for the `varyKey(context)` key-expression helper (the two lists are unioned), so a single read function keeps **separate cache variants per header value** without the runtime baking in any header convention. When the instance does not supply its own `Instance.Data["varyBy"]`, these domain-declared fields are used.

> **Reference:** issue [#839](https://github.com/burgan-tech/vnext/issues/839) — see also [Custom Functions → Cache](/docs/components/functions/custom).

### Flow-level cache config object (#846)

Author-controlled, flow-scoped cache tuning is now consolidated under a single **`attributes.config`** object. Its first member, **`functionCache.ttlSeconds`**, is the TTL for this workflow's built-in instance functions (`data`, `view`, `schema`, …).

```json
{ "config": { "functionCache": { "ttlSeconds": 120 } } }
```

- Repeat calls for the **same instance** are served from cache for the TTL window; when the **instance changes, the cache drops** and the next call is re-cached.
- The host default is **60s** when `config` (or `ttlSeconds`) is absent or non-positive.
- The **State Function is excluded** — the platform manages its cache separately (host-side `StateFunctionCache`).

> **Reference:** issue [#846](https://github.com/burgan-tech/vnext/issues/846) — see also [Workflow component → Config](/docs/components/workflow).

### Distributed resource locks — idempotent release + automatic cleanup (#840, #845)

Start, state-level, and shared transitions can declare an optional **`resourceLock`** block — a distributed lock (Dapr `lock.redis`, via the Aether SDK) that prevents concurrent instances from mutating a shared resource (a seat, daily limit, account, …). It runs in the **Manual** profile only, is owned by the `instanceId`, and always carries a TTL.

```json
{
"key": "reserve-daily-limit",
"target": "limit-reserved",
"triggerType": 0,
"versionStrategy": "Patch",
"resourceLock": {
"keyExpression": { "location": "./src/DailyLimitKey.csx", "code": "<base64>", "type": "L", "encoding": "NAT" },
"action": "Acquire",
"ttlSeconds": 300,
"onConflict": "Abort"
}
}
```

- The recommended model is **`Acquire` on the check/entry transition** and let the runtime release it: when the instance reaches a terminal state (Completed / Faulted / Cancelled), its locks are **released automatically** — no manual `Release` on every terminal transition.
- **`Release` is idempotent/best-effort** — `LockDoesNotExist` counts as success, a foreign lock is logged (not faulted), and lock cleanup never rolls back a successful business transition.
- A conflict on `Acquire` aborts the transition and returns **HTTP 409** (the instance is marked faulted `F`); the caller retries. `Extend` is unreliable (no native Dapr extend) — size the TTL to cover the whole operation.
- Subflows now derive a **per-subInstance lock key** so terminal-outcome propagation stays correct across nested instances ([#845](https://github.com/burgan-tech/vnext/issues/845)).

> **Reference:** issues [#840](https://github.com/burgan-tech/vnext/issues/840), [#845](https://github.com/burgan-tech/vnext/issues/845) — see also the new [Resource Lock guide](/docs/how-to/resource-lock) and [Workflow component → Transition](/docs/components/workflow).

### Wizard states behave like normal states in read functions (#838)

The State and View functions now treat **Wizard states like normal states**: authorization/role evaluation and the available-transition list are resolved the same way, so a wizard state's `state`/`view` response is consistent with any other state. This removes the special-casing that previously diverged wizard-state reads.

> **Reference:** issue [#838](https://github.com/burgan-tech/vnext/issues/838) — see also [Workflow component → Wizard State ve View Davranışı](/docs/components/workflow).

---

## Fixes

- **Author `Content-Type` preserved without charset suffix** — function output scripts that set `content-type` keep the author value verbatim (the `; charset=...` suffix is no longer appended) ([#847](https://github.com/burgan-tech/vnext/issues/847)).
- **Case-insensitive request-header normalization** — transition request headers are matched case-insensitively, so header lookups no longer miss on casing differences ([#841](https://github.com/burgan-tech/vnext/issues/841)).
- **Init mappings support for publish** — the init/publish path handles mapping components correctly ([#848](https://github.com/burgan-tech/vnext/issues/848)).

---

## Configuration Updates

Configuration for v0.0.76:

```json
{
"runtimeVersion": "0.0.76",
"schemaVersion": "0.0.51"
}
```

> **Note:** Schema version advances to **0.0.51** (from `0.0.50`). The bump adds the DaprConversation task type `"20"` ([#844](https://github.com/burgan-tech/vnext/issues/844)), the function-cache `varyByHeaders` / `varyByHeaderPrefixes` fields ([#839](https://github.com/burgan-tech/vnext/issues/839)), the workflow-level `attributes.config` object (`config.functionCache`) ([#846](https://github.com/burgan-tech/vnext/issues/846)), and the transition `resourceLock` definition ([#840](https://github.com/burgan-tech/vnext/issues/840)). Update `@burgan-tech/vnext-schema` in your domain project before validating against this runtime.

**Container images:** published at tag `0.0.76` under `ghcr.io/burgan-tech/vnext/*`, Cosign-signed (keyless OIDC) with SBOM + provenance. Immutable digests are listed in the [GitHub release](https://github.com/burgan-tech/vnext/releases/tag/v0.0.76).

---

## Issues Referenced

- [vnext #844](https://github.com/burgan-tech/vnext/issues/844) — Dapr Conversation (AI/LLM) task type.
- [vnext #839](https://github.com/burgan-tech/vnext/issues/839) — `varyKey` cache-key helper + config-driven vary-by headers.
- [vnext #846](https://github.com/burgan-tech/vnext/issues/846) — Consolidate flow-level `functionCache` under a `config` object.
- [vnext #840](https://github.com/burgan-tech/vnext/issues/840) — Resource lock: idempotent release + automatic terminal cleanup.
- [vnext #845](https://github.com/burgan-tech/vnext/issues/845) — Retryable 503 + per-subInstance lock key for terminal-outcome propagation.
- [vnext #838](https://github.com/burgan-tech/vnext/issues/838) — Treat wizard states like normal states in State/View functions.
- [vnext #847](https://github.com/burgan-tech/vnext/issues/847) — Preserve author `Content-Type` without charset suffix.
- [vnext #841](https://github.com/burgan-tech/vnext/issues/841) — Normalize request headers case-insensitively.
- [vnext #848](https://github.com/burgan-tech/vnext/issues/848) — Init mappings support for publish.
- [vnext-helm-charts #28](https://github.com/burgan-tech/vnext-helm-charts/pull/28) — Dapr Conversation helm component.

---

## Summary

- **Dapr Conversation task (Type 20)** invokes an LLM/AI provider provider-agnostically through the Dapr Conversation building block; the provider is a domain-owned Dapr component.
- **Function cache vary-by headers**: `varyByHeaders` / `varyByHeaderPrefixes` feed the `varyKey(context)` helper for per-header cache variants.
- **Flow-level `config.functionCache.ttlSeconds`**: author-controlled TTL for built-in instance functions (host default 60s; State Function excluded).
- **Distributed resource locks** are production-ready: idempotent release, automatic terminal cleanup, HTTP 409 on conflict, and a per-subInstance lock key for subflows.
- **Wizard states** are read like normal states in the State/View functions.
- **Fixes**: author `Content-Type` preserved, case-insensitive header matching, init mappings support for publish.
- **Schema** is **0.0.51**.

---

**vNext Runtime Platform Team**
Released July 27, 2026
6 changes: 6 additions & 0 deletions docs/components/functions/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ Cache, fonksiyon başına **opt-in**'dir ve yalnızca **yan etkisiz (read) fonks
| `bypassOnCacheError` | `boolean` | Hayır | `true` | `true`: cache okuma/yazma hataları isteği bozmaz, fonksiyon normal çalıştırılır. `false`: cache hatası isteği başarısız kılar |
| `generationKeyExpression` | `object` | Hayır | — | Generation stamp'inin tutulduğu state key'ini çözen Dynamic Expresso ifadesi. `generationKey`'den önceliklidir |
| `generationKey` | `string` | Hayır | — | Generation stamp'ini tutan statik state key'i |
| `varyByHeaders` | `string[]` | Hayır | — | Sonucu değiştiren **tam (exact)** request-header adları. Belirtilen header'lar cache key'ine katılır |
| `varyByHeaderPrefixes` | `string[]` | Hayır | — | Sonucu değiştiren request-header adı **prefiksleri** (ör. bir prefix ile başlayan tüm header'lar) |

:::tip Vary-by header'lar (`#839`)
`varyByHeaders` ve `varyByHeaderPrefixes`, key-expression içinde kullanılabilen `varyKey(context)` yardımcı fonksiyonunun header-adı kümesini oluşturur (ikisi birleştirilir). Böylece runtime herhangi bir header konvansiyonuna bağlı kalmadan; aynı fonksiyonun farklı header değerleri için **ayrı cache varyantları** tutar. Instance `Instance.Data["varyBy"]` ile kendi kümesini sağlamazsa, domain'de tanımlı bu iki alan kullanılır.
:::

### Generation-Namespace Invalidation

Expand Down
141 changes: 141 additions & 0 deletions docs/components/tasks/dapr-conversation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
sidebar_position: 15
title: Dapr Conversation Task
description: Dapr Conversation building block üzerinden LLM/AI sağlayıcı çağrısı yapan task
---

# Dapr Conversation Task (Type: `20`)

Dapr Conversation Task (`type: "20"`), bir **LLM/AI sağlayıcısını** Dapr'ın **Conversation building block**'u üzerinden çağırır. Böylece iş akışları; OpenAI, Anthropic, AWS Bedrock gibi sağlayıcılara **sağlayıcı-bağımsız** tek bir arayüzle prompt gönderebilir — diğer Dapr task türlerinin kullandığı aynı Dapr sidecar üzerinden.

Sağlayıcı seçimi ve kimlik bilgileri **domain'e ait** bir Dapr `conversation` component'inde (ör. `openai`) tanımlanır; task yalnızca o component'in adını (`componentName`) ve mesajları (`inputs`) referans verir. Runtime bileşenini eklemek için bkz. [vnext-helm-charts #28](https://github.com/burgan-tech/vnext-helm-charts/pull/28).

## Görev Tanımı

> **Schema:** `task-definition.schema.json`

```json
{
"key": "summarize-complaint",
"version": "1.0.0",
"domain": "core",
"flow": "sys-tasks",
"flowVersion": "1.0.0",
"tags": ["ai", "llm", "conversation"],
"attributes": {
"type": "20",
"config": {
"componentName": "openai",
"inputs": [
{ "role": "system", "content": "Sen bir müşteri şikayeti özetleyicisisin." },
{ "role": "user", "content": "Şikayet metnini 2 cümlede özetle." }
],
"parameters": {
"model": "gpt-4o-mini",
"maxTokens": "512"
},
"temperature": 0.2,
"scrubPII": true,
"timeoutSeconds": 30
}
}
}
```

## Konfigürasyon Alanları

| Alan | Tip | Zorunlu | Varsayılan | Açıklama |
|------|-----|---------|------------|----------|
| `componentName` | string | **Evet** | — | Dapr conversation component adı (yapılandırılmış LLM sağlayıcısı), ör. `openai` |
| `inputs` | array | Hayır | — | Konuşma girdileri: `role`/`content` mesajlarından oluşan dizi (aşağıya bakın) |
| `parameters` | object | Hayır | — | Sağlayıcıya özgü **string** parametreler (ör. `model`, `maxTokens`). Component'e olduğu gibi iletilir |
| `metadata` | object | Hayır | — | İstekle iletilen Dapr component metadata'sı (string değerler) |
| `contextId` | string | Hayır | — | Durumlu (stateful) bir konuşmayı sürdürmek için bağlam tanımlayıcısı |
| `temperature` | number | Hayır | — | Örnekleme sıcaklığı (sampling temperature) |
| `scrubPII` | boolean | Hayır | — | `true` ise sağlayıcıdan prompt ve yanıtlarda PII temizliği (scrub) istenir |
| `timeoutSeconds` | integer | Hayır | `30` | Timeout süresi (saniye, minimum: 1) |

### `inputs` Mesaj Yapısı

Her girdi bir rol/içerik mesajıdır:

```json
{ "role": "user", "content": "...", "scrubPII": true, "name": "opsiyonel" }
```

| Alan | Değerler | Açıklama |
|------|----------|----------|
| `role` | `user`, `system`, `assistant`, `developer`, `tool` | Mesajın rolü |
| `content` | string | Mesaj içeriği |
| `scrubPII` | boolean (ops.) | Bu mesaj için PII temizliği |
| `name` | string (ops.) | Mesaj için opsiyonel ad |

## Property Erişimi

Değerler çoğunlukla statik konfigürasyon yerine input mapping içinde dinamik olarak atanır.

| Property | Setter Metodu | Açıklama |
|----------|---------------|----------|
| `ComponentName` | `SetComponentName(string componentName)` | Conversation component adı |
| `Inputs` | `SetInputs(dynamic inputs)` | Mesaj dizisi |
| `Parameters` | `SetParameters(Dictionary<string, string?> parameters)` | Sağlayıcı parametreleri |
| `Metadata` | `SetMetadata(Dictionary<string, string?> metadata)` | Component metadata'sı |
| `ContextId` | `SetContextId(string? contextId)` | Bağlam tanımlayıcısı |
| `Temperature` | `SetTemperature(double? temperature)` | Örnekleme sıcaklığı |
| `ScrubPII` | `SetScrubPII(bool? scrubPII)` | PII temizliği |
| `TimeoutSeconds` | `SetTimeoutSeconds(int? timeoutSeconds)` | Timeout süresi |

Input mapping örneği:

```csharp
public class SummarizeComplaintMapping : ScriptBase, IMapping
{
public Task<ScriptResponse> InputHandler(WorkflowTask task, ScriptContext context)
{
var conversation = task as DaprConversationTask;
conversation.SetComponentName("openai");
conversation.SetInputs(new[]
{
new { role = "system", content = "Sen bir müşteri şikayeti özetleyicisisin." },
new { role = "user", content = (string)context.Instance.Data.complaintText }
});
conversation.SetParameters(new Dictionary<string, string?>
{
["model"] = "gpt-4o-mini",
["maxTokens"] = "512"
});
return Task.FromResult(new ScriptResponse());
}

public Task<ScriptResponse> OutputHandler(ScriptContext context)
{
var summary = context.Body?.outputs?[0]?.result;
return Task.FromResult(new ScriptResponse { Data = new { summary } });
}
}
```

## Standart Yanıt

Yanıt, sağlayıcının döndürdüğü çıktı(lar)ı `outputs` altında taşır:

```json
{
"Data": {
"outputs": [
{ "result": "Müşteri, kartının teslim edilmediğini ve çağrı merkezine ulaşamadığını bildiriyor." }
]
},
"StatusCode": 200,
"IsSuccess": true,
"ErrorMessage": null,
"TaskType": "20"
}
```

## İlgili

- [Tasks Genel Bakış](/docs/components/tasks/) — tüm task türleri ve referans mekanizması
- [DaprService Task](/docs/components/tasks/dapr-service) — aynı Dapr sidecar üzerinden service invocation
- Schema kaynağı: [task-definition.schema.json (vnext-schema)](https://github.com/burgan-tech/vnext-schema/blob/master/schemas/task-definition.schema.json)
- Helm component: [vnext-helm-charts #28](https://github.com/burgan-tech/vnext-helm-charts/pull/28)
3 changes: 2 additions & 1 deletion docs/components/tasks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Her task tanımı `task-definition.schema.json` şemasına uyar. Zorunlu alanlar

## Görev Türleri

`task-definition.schema.json` toplamda **19 task türü** tanımlar:
`task-definition.schema.json` toplamda **20 task türü** tanımlar:

| # | Görev Türü | Açıklama | Detay |
|---|---|---|---|
Expand All @@ -76,6 +76,7 @@ Her task tanımı `task-definition.schema.json` şemasına uyar. Zorunlu alanlar
| 17 | **StateStoreTask** | Dapr state store ile cache (get/set/delete) | [StateStore](./state-store) |
| 18 | **CacheAsideTask** | Read-through cache (miss'te sourceTask çalıştırıp cache'ler) | [CacheAside](./cache-aside) |
| 19 | **GetInstanceTask** | Tek bir instance'ın tam projeksiyonunu (metadata + data) çekme | [GetInstance](./get-instance) |
| 20 | **DaprConversationTask** | Dapr Conversation ile LLM/AI sağlayıcı çağrısı | [DaprConversation](./dapr-conversation) |

## Görev Kullanımı

Expand Down
Loading
Loading