GenAI has transformed nearly every industry. Our research dives into developer use cases and sentiments on its organizational impact.
Internal Developer Portals are reshaping the developer experience. What's the best way to get started? Do you build or buy? Tune in to see.
Perfecting CRUD Functionality in NextJS
OpenCV Integration With Live 360 Video for Robotics
Observability and Performance
The dawn of observability across the software ecosystem has fully disrupted standard performance monitoring and management. Enhancing these approaches with sophisticated, data-driven, and automated insights allows your organization to better identify anomalies and incidents across applications and wider systems. While monitoring and standard performance practices are still necessary, they now serve to complement organizations' comprehensive observability strategies. This year's Observability and Performance Trend Report moves beyond metrics, logs, and traces — we dive into essential topics around full-stack observability, like security considerations, AIOps, the future of hybrid and cloud-native observability, and much more.
Getting Started With Agentic AI
Java Application Containerization and Deployment
DZone events bring together industry leaders, innovators, and peers to explore the latest trends, share insights, and tackle industry challenges. From Virtual Roundtables to Fireside Chats, our events cover a wide range of topics, each tailored to provide you, our DZone audience, with practical knowledge, meaningful discussions, and support for your professional growth. DZone Events Happening Soon Below, you’ll find upcoming events that you won't want to miss. Modernizing Enterprise Java Applications: Jakarta EE, Spring Boot, and AI Integration Date: February 25, 2025Time: 1:00 PM ET Register for Free! Unlock the potential of AI integration in your enterprise Java applications with our upcoming webinar! Join Payara and DZone to explore how to enhance your Spring Boot and Jakarta EE systems using generative AI tools like Spring AI and REST client patterns. What to Consider When Building an IDP Date: March 4, 2025Time: 1:00 PM ET Register for Free! Is your development team bogged down by manual tasks and “TicketOps”? Internal Developer Portals (IDPs) streamline onboarding, automate workflows, and enhance productivity—but should you build or buy? Join Harness and DZone for a webinar to explore key IDP capabilities, compare Backstage vs. managed solutions, and learn how to drive adoption while balancing cost and flexibility. DevOps for Oracle Applications with FlexDeploy: Automation nd Compliance Made Easy Date: March 11, 2025Time: 1:00 PM ET Register for Free! Join Flexagon and DZone as Flexagon's CEO unveils how FlexDeploy is helping organizations future-proof their DevOps strategy for Oracle Applications and Infrastructure. Explore innovations for automation through compliance, along with real-world success stories from companies who have adopted FlexDeploy. Make AI Your App Development Advantage: Learn Why and How Date: March 12, 2025Time: 10:00 AM ET Register for Free! The future of app development is here, and AI is leading the charge. Join Outsystems and DZone, on March 12th at 10am ET, for an exclusive Webinar with Luis Blando, CPTO of OutSystems, and John Rymer, industry analyst at Analysis.Tech, as they discuss how AI and low-code are revolutionizing development.You will also hear from David Gilkey, Leader of Solution Architecture, Americas East at OutSystems, and Roy van de Kerkhof, Director at NovioQ. This session will give you the tools and knowledge you need to accelerate your development and stay ahead of the curve in the ever-evolving tech landscape. Developer Experience: The Coalescence of Developer Productivity, Process Satisfaction, and Platform Engineering Date: March 12, 2025Time: 1:00 PM ET Register for Free! Explore the future of developer experience at DZone’s Virtual Roundtable, where a panel will dive into key insights from the 2025 Developer Experience Trend Report. Discover how AI, automation, and developer-centric strategies are shaping workflows, productivity, and satisfaction. Don’t miss this opportunity to connect with industry experts and peers shaping the next chapter of software development. Unpacking the 2025 Developer Experience Trends Report: Insights, Gaps, and Putting it into Action Date: March 19, 2025Time: 1:00 PM ET Register for Free! We’ve just seen the 2025 Developer Experience Trends Report from DZone, and while it shines a light on important themes like platform engineering, developer advocacy, and productivity metrics, there are some key gaps that deserve attention. Join Cortex Co-founders Anish Dhar and Ganesh Datta for a special webinar, hosted in partnership with DZone, where they’ll dive into what the report gets right—and challenge the assumptions shaping the DevEx conversation. Their take? Developer experience is grounded in clear ownership. Without ownership clarity, teams face accountability challenges, cognitive overload, and inconsistent standards, ultimately hampering productivity. Don’t miss this deep dive into the trends shaping your team’s future. What's Next? DZone has more in store! Stay tuned for announcements about upcoming Webinars, Virtual Roundtables, Fireside Chats, and other developer-focused events. Whether you’re looking to sharpen your skills, explore new tools, or connect with industry leaders, there’s always something exciting on the horizon. Don’t miss out — save this article and check back often for updates!
Microsoft Azure SQL is a robust, fully managed database platform designed for high-performance querying, relational data storage, and analytics. For a typical web application with a backend, it is a good choice when we want to consider a managed database that can scale both vertically and horizontally. An application software generates user metrics on a daily basis, which can be used for reports or analytics. Azure SQL is a great choice to consider for storing and querying this data under certain conditions: The analytical queries require joins with other tables (applying filters on UI)You want to combine historical and transactional dataThe data volume is not extremely large, and query performance can be managed by tuning Let's consider an example of a hotel booking site running Azure SQL in the backend. We want to see a UI dashboard for tracking user activity, such as clicks on the site, visits to the hotel description page, bookings made, etc. Let's assume all this telemetry data is dumped for each user on a daily basis in unstructured storage, and we are pulling this data into our database using background jobs, such as Apache Airflow. Below is the schema for users table and a table to store daily metrics. MS SQL CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, age INT , city VARCHAR(100), country VARCHAR(100), currency VARCHAR(10), last_login VARCHAR, hotel_preference VARCHAR(100) ); CREATE TABLE daily_user_metrics ( id BIGINT IDENTITY PRIMARY KEY, -- Unique identifier for the record user_id BIGINT NOT NULL, -- Foreign key to the users table clicks INT DEFAULT 0, -- Number of total site clicks visits INT DEFAULT 0, -- Number of visits to the hotel page bookings INT DEFAULT 0, -- Number of bookings reviews INT DEFAULT 0, -- Number of reviews cancellations INT DEFAULT 0, -- Number of cancellations date_created DATE, -- Daily metrics for each user ); You can draw many insights from the above tables. Let's consider one particular example. We need to aggregate daily activity metrics grouped by date in descending order for customers between the ages of 30 and 40 located in New York City. Below is the query: MS SQL SELECT date_created, SUM(clicks) AS total_clicks, SUM(visits) AS total_visits, SUM(bookings) AS total_bookings, SUM(reviews) AS total_reviews, SUM(cancellations) AS total_cancellations, FROM daily_user_metrics m INNER JOIN users u on m.user_id = u.id WHERE u.age BETWEEN 30 and 40 and u.city ='New York' and m.date_created BETWEEN :startDate and :endDate GROUP BY date_created ORDER BY date_created DESC Now, we can analyze the daily trends from this group of users who are in New York and between the ages of 30 and 40. The table is mostly performant, and we are able to easily perform range queries spread across multiple months. Eventually, our requirements grow. We now want to track user behavior in a weekly or monthly range. But our table stores the data on a daily basis. We now have two options: Query the table and group the date_created weekly or monthly, depending on the ask. Create a couple of views that aggregate the data on a weekly or monthly basis per user. See the query below: MS SQL CREATE VIEW weekly_user_metrics AS SELECT DATEADD(DAY, -(DATEPART(WEEKDAY, date) - 1), date) AS week_start, -- Start of the week (Sunday) SUM(clicks) AS total_clicks, SUM(visits) AS total_visits, SUM(bookings) AS total_bookings, SUM(reviews) AS total_reviews, SUM(cancellations) AS total_cancellations, FROM daily_user_metrics m INNER JOIN users u on m.user_id = u.id WHERE u.age BETWEEN 30 and 40 and u.city ='New York' and m.date_created BETWEEN :startDate and :endDate GROUP BY DATEADD(DAY, -(DATEPART(WEEKDAY, date) - 1), date) -- Group by week start ORDER BY DATEADD(DAY, -(DATEPART(WEEKDAY, date) - 1), date) DESC -- Sort by latest week However, one important thing to consider is that views just provide an abstraction to the underlying query which simply queries the underlying table. Materialized Views are the next thought that comes to mind. However, they need to be refreshed manually or on a schedule, due to which real-time data is not available. To address these issues, Azure SQL Server offers a great feature known as Indexed View. An Indexed View is a physical representation of a view stored in the database with a unique clustered index. Changes to the underlying tables automatically update the indexed view to keep it in sync. It uses a clustered index that organizes the data in the view based on the order of the index keys. The indexed view is ideal for scenarios where we need real-time data, and our query involves complex multi-table joins. It is also suitable for our use case where existing data is rarely updated but queried often, and we have range-based queries and want to do ordered retrieval. There are some things to consider before deciding whether you want to go for indexed views. Indexed views cannot have non-deterministic functions. A non-deterministic function is a function that does not always return the same result for the same input, even when executed with identical arguments and under the same database conditions. Also, an indexed view is an actual structure that requires storage, similar to a Materialized View. The syntax for creating an Indexed View is similar to the View creation query above. However, we cannot have non-deterministic functions while creating an indexed view. The line DATEADD(DAY, -(DATEPART(WEEKDAY, date) - 1), date) AS week_start in the view query above depends on the session-specific SET DATEFIRST setting, which determines the first day of the week. This is considered non-deterministic as it will produce different results for different conditions. Keeping the above things in mind, we can proceed to eliminate the non-deterministic computation by making the column deterministic. We add a week_start column to the underlying table and precompute and fill the week_start value in the table for daily data pulls. So, the rows with dates D1 through D7 belong to W1, D8 to D14 belong to W2, and so on. Now, we can proceed to create an indexed view with the SQL below. MS SQL ALTER TABLE daily_user_metrics ADD week_start DATE;-- Populate this column with first day of the week going forward CREATE VIEW dbo.weekly_user_metric_aggregations_view WITH SCHEMABINDING AS SELECT user_id, week_start, SUM(clicks) AS total_clicks, SUM(visits) AS total_visits, SUM(bookings) AS total_bookings, SUM(reviews) AS total_reviews, SUM(cancellations) AS total_cancellations, COUNT_BIG(*) AS row_count --SQL Server requires COUNT_BIG(*) in indexed views to handle scenarios where the count exceeds the range of an INT data type. FROM dbo.daily_user_metrics GROUP BY user_id, week_start; CREATE UNIQUE CLUSTERED INDEX IX_weekly_user_metric_aggregations_view ON dbo.weekly_user_metric_aggregations_view (user_id, week_start); After this indexed view is created, we can query it as follows: MS SQL SELECT week_start, SUM(total_clicks) AS total_clicks, SUM(total_visits) AS total_visits, SUM(total_bookings) AS total_bookings, SUM(total_reviews) AS total_reviews, SUM(total_cancellations) AS total_cancellations, FROM weekly_user_metric_aggregations_view mv INNER JOIN users u on mv.user_id = u.id WHERE u.age BETWEEN 30 and 40 and u.city ='New York' and m.date_created BETWEEN :startDate and :endDate GROUP BY week_created ORDER BY week_created DESC Conclusion An indexed view will have a significantly faster query time than a basic view or querying the table directly for large datasets where data has grown up to a million rows or more. The end user will have a low latency experience, and queries to the database will be optimized. Since we aggregated a week's worth of data in 1 row, we have cut the aggregation time taken to 1/7th.
The system design of an audio streaming app is unique in how it deals with idiosyncratic business needs. Typically, audio streaming requires a large amount of data to be transferred within the limited bandwidth of the network communication channel. A successful audio streaming service must handle millions of active users and thousands of content providers from various geographical locations. Different devices and platforms (smartphone, desktop, smart speaker) may support different audio formats such as MP3, FLAC, ALAC, and so on. In this blog post, we will explore the nuances of designing such a complex system covering functional and non-functional requirements. Functional Requirements The functional requirements will cover features required for the content creators and audio users or listeners. Content management. The content creator can upload, delete, or update audio with any format. Each audio should have metadata such as title, author, description, category, and metadata tags.Notification. The content creator receives notifications on the success or failure of the audio uploaded. The audio listener or user receives notifications on the successful upload of audio.Seamless streaming. Users can play, pause, rewind, and download audio of their choice of format.Subscribe. Users should be able to subscribe to the audio of their choice to receive notifications on new or updated content.User login. Content creators must be authenticated and authorized to access the system. Users can register with the system to set up their profiles. Search. Users should be able to search audio based on certain attributes or metadata. Note: Live streaming and payment services are out of this article's scope. Sequence Diagram of the Functional Use Case Architectural Design Let’s define service components to support the functional requirements. The first functional requirement is to upload audio using any format such as MP3, FLAC, ALAC, etc. These audio files can hold an impressive amount of data. The audio codec plays a crucial role in efficiently storing, transmitting, and playing this data. There are mainly two types of codecs: Lossless codec – compress audio without losing any data and can be fully restored without losing quality.Lossy codec – removes some data which helps in reducing size significantly. This could compromise sound quality. The content provider typically works with lossless formats for recording and editing. This ensures no loss of quality as they operate the audio, apply effects, and master the final product. In simple terms, audio mastering is the final step in the audio production process. Once the final audio has been mastered, it is converted to a lossy format for general distribution. With a lossy format, the size gets reduced to a greater extent, making it easier and faster to stream and download. The compressed audio needs to be transmitted to the listener device with the supported network bandwidth. The bandwidth could vary dynamically, with the network load changing based on the number of users getting connected or the user moving from one network zone to another while listening to the audio. To support this changing network bandwidth, the codec could use the "adaptive bit rate" mechanism. Adaptive bitrate streaming detects users' bandwidth in real time, adjusting the stream accordingly. It can dynamically switch the bit rate streaming based on the current bandwidth of the user. This results in little buffering, faster start times, and a good experience for both high-end and low-end connections. The encoder encodes the single source of the audio file at multiple-bit rates. These streams of bytes are packaged and stored in the object store to be available to the listener. Once the audio is successfully uploaded, the Notification service sends a notification to the content provider. The content creator additionally provides metadata while requesting to upload the audio, which can be directly saved to a NoSQL DB via Audio Data service. This data will be indexed to offer better searching capability to the audio listener. The audio system needs an authentication and authorization service to handle new user registration, log-in using the user credential, and authorization based on roles associated with the users (listeners and content providers). API Gateway service that can centrally manage authentication and authorization. This service can be leveraged to perform the request-routing, and orchestrating process flow from front-end to back-end service. The audio user (listener) could search for audio of interest, which will be forwarded to the Audio Data service to return the location of the relevant audio(s), pulling info from the audio metadata store. The user clicks on the link, and it will return the audio bytes packaged and stored by the Package service. The User Profile service manages user preferences, followers, and followings. The above diagram depicts the basic "upload audio" process flow triggered by the content provider and the "listen audio" process flow triggered by the audio user/listener. The pipeline and filtering design pattern with message queuing is being used to support the data flow between services to scale the process efficiently with parallelism and fault tolerance. Now, let’s move to non-functional requirements. Non-Functional Requirements Scalability. The system should support thousands of concurrent users and be able to grow and shrink.Fault tolerance. The system should be fault-tolerant, usually with redundant data, with low downtime.Performance. The system should have low latency and high responsiveness during content playback.Security. The system must be safe from unauthorized access or harmful attacks such as DDoS. Scalability The audio system should be able to support thousands of active content creators. To manage the load, the design includes running multiple instances of API Gateway service, App Web service, and Audio Data service fronted by load balancers. However, this will be not scalable with the increase in number of content creators. The audio files are usually large, which will use high network and computing power while traversing through multiple service components. To optimize system resource usage, a signed URL (also referred to as a pre-signed URL) can be used to provide time-limited access to the object store to upload audio files directly. This eliminates the need to route traffic via API Gateway and API Web service. The signed URLs can be configured with granular permissions and expiration rules for better security. This covers the scalability requirement for uploading the audio files by content providers. Millions of search requests from the audio listeners could hit the system, causing the Audio Data service to be overloaded. To scale the system supporting this massive search operation, the Content Query Responsibility Segregation (CQRS) design pattern can be used. The read and write operations from/to datastore can be managed independently, supporting different latency, scalability, and security requirements. Fault Tolerance There are various dimensions to the fault tolerance design. A few of them are already included in the design. Use of multiple instances of service for scalability and fault toleranceMessage queuing with pipeline processing to support fault tolerance at the data flow levelSeparation of transcoder service and packaging serviceMultiple DB instances with replicaAvailability Zones aligning to geographical regions such as the U.S. East, U.S. West, and Asia Pacific for the deployment of the entire system supporting a specific region and userbase. Performance A content delivery network (CDN) is a distributed network of servers that are grouped together to deliver content, such as audio and video files, faster and more efficiently. It caches the content at the edge server to provide better performance with low latency. Security CDN also improves security by providing DDoS mitigation and a few other security measurements. The Package service will distribute the audio files to CDNs to be cached at the edge servers. The Audio Data service will update the location of the CDN to its metadata that will be routed to the Search service for querying by users. The above diagram depicts the high-level component architecture of a typical audio system. Conclusion At the heart of a good audio system are scalability, performance, and fault tolerance to provide a good user experience with minimal distortion, low latency, and reliability.
The year 2025 is the year of AI agents. For the purposes of this article, an AI agent is a system that can leverage AI to achieve a goal by following a series of steps, possibly reasoning on its results and making corrections. In practice, the steps that an agent follows can constitute a graph. We will build a reactive agent (meaning that it reacts to a stimulus, in our case, the input from a user) to help people find their perfect vacation. Our agent will find the best city in the specified country, considering the food, sea, and activity specified by the user. The agent will look like this: In the first phase, it will collect information in parallel, ranking the cities by a single characteristic. The last step will use this information to choose the best city. You could use a search engine to collect information, but we will use ChatGPT for all the steps, though we will use different models. You could write all the code by hand or use some library to help you simplify the code a bit. Today, we will use a new feature that I added to Fibry, my Actor System, to implement the graph and control the parallelism in great detail. Fibry is a simple and small Actor System that provides an easy way to leverage actors to simplify multi-threading code and does not have any dependency. Fibry also implements a Finite State Machine, so I decided to extend it to make it easier to write agents in Java. My inspiration has been LangGraph. As Fibry is about multi-threading, the new features allow plenty of flexibility in deciding the level of parallelism while keeping everything as simple as possible. You should use Fibry 3.0.2, for example: Plain Text compile group: 'eu.lucaventuri', name: 'fibry', version: '3.0.2' Defining the Prompts The first step is defining the prompts that we need for the LLM: Java public static class AiAgentVacations { private static final String promptFood = "You are a foodie from {country}. Please tell me the top 10 cities for food in {country}."; private static final String promptActivity = "You are from {country}, and know it inside out. Please tell me the top 10 cities in {country} where I can {goal}"; private static final String promptSea = "You are an expert traveler, and you {country} inside out. Please tell me the top 10 cities for sea vacations in {country}."; private static final String promptChoice = """ You enjoy traveling, eating good food and staying at the sea, but you also want to {activity}. Please analyze the following suggestions from your friends for a vacation in {country} and choose the best city to visit, offering the best mix of food and sea and where you can {activity}. Food suggestions: {food}. Activity suggestions: {activity}. Sea suggestions: {sea}. """; } Defining the States Normally, you would define four states, one for each step. However, since branching out and back is quite common, I added a feature to handle this with only a single state. As a result, we need only two states: CITIES, where we collect information, and CHOICE, where we choose the city. Plain Text enum VacationStates { CITIES, CHOICE } Defining the Context The different steps of the agent will collect information that needs to be stored somewhere; let’s call it context. Ideally, you would want every step to be independent and know as little as possible of the other, but achieving this in a simple way, with a low amount of code while keeping as much type safety as possible and maintaining thread safety, is not exactly straightforward. As a result, I choose to force the context to be a record, providing some functionality to update the values of the record (using reflection underneath) while we wait for JEP 468 (Derived Record Creation) to be implemented. Java public record VacationContext(String country, String goal, String food, String activity, String sea, String proposal) { public static VacationContext from(String country, String goal) { return new VacationContext(country, goal, null, null, null, null); } } Defining the Nodes Now, we can define the logic of the agent. We will allow the user to use two different LLM models, for example, a “normal” LLM for the search and a “reasoning” one for the choice step. This is where things become a bit trickier, as it is quite dense: Java AgentNode<VacationStates, VacationContext> nodeFood = state -> state.setAttribute("food", modelSearch.call("user", replaceField(promptFood, state.data(), "country"))); AgentNode<VacationStates, VacationContext> nodeActivity = state -> state.setAttribute("activity", modelSearch.call("user", replaceField(promptActivity, state.data(), "country"))); AgentNode<VacationStates, VacationContext> nodeSea = state -> state.setAttribute("sea", modelSearch.call("user", replaceField(promptSea, state.data(), "country"))); AgentNode<VacationStates, VacationContext> nodeChoice = state -> { var prompt = replaceAllFields(promptChoice, state.data()); System.out.println("***** CHOICE PROMPT: " + prompt); return state.setAttribute("proposal", modelThink.call("user", prompt)); }; As you might have guessed, modelSearch is the model used for search (e.g., ChatGPT 4o), and modelThink could be a “reasoning model” (e.g., ChatGPT o1). Fibry provides a simple LLM interface and a simple implementation for ChatGPT, exposed by the class ChatGpt. Please note that calling ChatPGT API requires an API key that you need to define using the “-DOPENAI_API_KEY=xxxx” JVM parameter. Different and more advanced use cases will require custom implementations or the usage of a library. There is also a small issue related to the philosophy of Fibry, as Fibry is meant not to have any dependencies, and this gets tricky with JSON. As a result, now Fibry can operate in two ways: If Jackson is detected, Fibry will use it with reflection to parse JSON.If Jackson is not detected, a very simple custom parser (that seems to work with ChatGPT output) is used. This is recommended only for quick tests, not for production.Alternatively, you can provide your own JSON processor implementation and call JsonUtils.setProcessor(), possibly checking JacksonProcessor for inspiration. The replaceField() and replaceAllFields() methods are defined by RecordUtils and are just convenience methods to replace text in the prompt, so that we can provide our data to the LLM.The setAttribute() function is used to set the value of an attribute in the state without you having to manually recreate the record or define a list of “withers” methods. There are other methods that you might use, like mergeAttribute(), addToList(), addToSet(), and addToMap(). Building the Agent Now that we have the logic, we need to describe the graph of dependencies between states and specify the parallelism we want to achieve. If you imagine a big multi-agent system in production, being able to express the parallelism required to maximize performance without exhausting resources, hitting rate limiters, or exceeding the parallelism allowed by external systems is a critical feature. This is where Fibry can help, making everything explicit but relatively easy to set up. Let’s start creating the agent builder: Plain Text var builder = AiAgent.<VacationStates, VacationContext>builder(true); The parameter autoGuards is used to put automatic guards on the states, which means that they are executed with an AND logic, and a state is executed only after all the incoming states have been processed. If the parameter is false, the state is called once for each incoming state. In the previous example, if the intention is to execute D once after A and once after C, then autoGuards should be false, while if you want it to be called only once after both have been executed, then autoGuards should be true. But let’s continue with the vacation agent. Plain Text builder.addState(VacationStates.CHOICE, null, 1, nodeChoice, null); Let’s start with the method addState(). It is used to specify that a certain state should be followed by another state and execute a certain logic. In addition, you can specify the parallelism (more on that soon) and the guards. In this case: The state is CHOICEThere is no default following state (e.g., this is a final state)The parallelism is 1There is no guard The next state is just a default because the node has the possibility to overwrite the next state, which means that the graph can dynamically change at runtime, and in particular, it can perform cycles, for example, if some steps need to be repeated to collect more or better information. This is an advanced use case. An unexpected concept might be the parallelism. This has no consequences in a single run of the agent, but it is meaningful in production at scale. In Fibry, every node is backed by an actor, which, from a practical point of view, is a thread with a list of messages to process. Every message is an execution step. So parallelism is the number of messages that can be executed at a single time. In practice: parallelism == 1 means there is only one thread managing the step, so only one execution at a time.parallelism > 1 means that there is a thread pool backing the actor, with the number of threads specified by the user. By default, it uses virtual threads.parallelism == 0 means that every message creates a new actor backed by a virtual thread, so the parallelism can be as high as necessary. Every step can be configured configured independently, which should allow you to configure performance and resource usage quite well. Please consider that if parallelism != 1, you might have multi-threading, as the thread confinement typically associated with actors is lost. This was a lot to digest. If it is clear, you can check state compression. State Compression As said earlier, it is quite common to have a few states that are related to each other, they need to be performed in parallel and join before moving to a common state. In this case, you do not need to define multiple states, but you can use only one: Plain Text builder.addStateParallel(VacationStates.CITIES, VacationStates.CHOICE, 1, List.of(nodeFood, nodeActivity, nodeSea), null); In this case, we see that the CITIES state is defined by three nodes, and addStateParallel() takes care of executing them in parallel and waits for the execution of all of them to be finished. In this case, the parallelism is applied to each node, so in this case, you will get three single-thread actors. Please note that if you do not use autoGuards, this basically allows you to mix OR and AND logic. In case you want to merge some nodes in the same state, but they need to be executed serially (e.g., because they need information generated by the previous node), the addStateSerial() method is also available. AIAgent creation is simple, but there are a few parameters to specify: The initial stateThe final state (which can be null)A flag to execute states in parallel when possible Plain Text var vacationAgent = builder.build(VacationStates.CITIES, null, true); Now we have an agent, and we can use it, calling process: Plain Text vacationsAgent.process(AiAgentVacations.VacationContext.from("Italy", "Dance Salsa and Bachata"), (state, info) -> System.out.println(state + ": " + info)); This version of process() takes two parameters: The initial state, which contains the information required by the agent to perform its actionsAn optional listener, for example, if you want to print the output of each step If you need to start the action and check its return value, later, you can use processAsync(). If you are interested in learning more about the parallelism options, I recommend you check the unit test TestAIAgent. It simulates an agent with nodes that sleep for a while and can help you see the impact of each choice: But I promised you a multi-agent, didn’t I? Extending to Multi-Agents The AIAgent that you just created is an actor, so it runs on its own thread (plus all the threads used by the nodes), and it also implements the Function interface, in case you need it. There is actually nothing special about a multi-agent; just one or more nodes of an agent ask another agent to perform an action. However, you can build a library of agents and combine them in the best way while simplifying the whole system. Let’s imagine that we want to leverage the output of our previous agent and use it to calculate how much that vacation would cost so the user can decide if it is affordable enough. Like a real Travel Agent! This is what we want to build: First, we need prompts to extract the destination and compute the cost. Java private static final String promptDestination = "Read the following text describing a destination for a vacation and extract the destination as a simple city and country, no preamble. Just the city and the country. {proposal}"; private static final String promptCost = "You are an expert travel agent. A customer asked you to estimate the cost of travelling from {startCity}, {startCountry} to {destination}, for {adults} adults and {kids} kids}"; We just need two states, one to research the cities, which is done by the previous agent, and one to calculate the cost. Plain Text enum TravelStates { SEARCH, CALCULATE } We also need a context, that should also hold the proposal from the previous agent. Plain Text public record TravelContext(String startCity, String startCountry, int adults, int kids, String destination, String cost, String proposal) { } Then we can define the agent logic, which requires as a parameter another agent. The first node calls the previous agent to get the proposal. Java var builder = AiAgent.<TravelStates, TravelContext>builder(false); AgentNode<TravelStates, TravelContext> nodeSearch = state -> { var vacationProposal = vacationsAgent.process(AiAgentVacations.VacationContext.from(country, goal), 1, TimeUnit.MINUTES, (st, info) -> System.out.print(debugSubAgentStates ? st + ": " + info : "")); return state.setAttribute("proposal", vacationProposal.proposal()) .setAttribute("destination", model.call(promptDestination.replaceAll("\\{proposal\\}", vacationProposal.proposal()))); }; The second node computes the cost: Plain Text AgentNode<TravelStates, TravelContext> nodeCalculateCost = state -> state.setAttribute("cost", model.call(replaceAllFields(promptCost, state.data()))); Then, we can define the graph and build the agent Java builder.addState(TravelStates.SEARCH, TravelStates.CALCULATE, 1, nodeSearch, null); builder.addState(TravelStates.CALCULATE, null, 1, nodeCalculateCost, null); var agent = builder.build(TravelStates.SEARCH, null, false); Now we can instantiate the two agents (I chose to use ChatGPT 4o and ChatGPT 01-mini) and use them: Java try (var vacationsAgent = AiAgentVacations.buildAgent(ChatGPT.GPT_MODEL_4O, ChatGPT.GPT_MODEL_O1_MINI)) { try (var travelAgent = AiAgentTravelAgency.buildAgent(ChatGPT.GPT_MODEL_4O, vacationsAgent, "Italy", "Dance Salsa and Bachata", true)) { var result = travelAgent.process(new AiAgentTravelAgency.TravelContext("Oslo", "Norway", 2, 2, null, null, null), (state, info) -> System.out.println(state + ": " + info)); System.out.println("*** Proposal: " + result.proposal()); System.out.println("\n\n\n*** Destination: " + result.destination()); System.out.println("\n\n\n*** Cost: " + result.cost()); } } Final Outputs If you wonder what the result is, here is the long output that you can get when stating that what you want to do is to dance Salsa and Bachata: Destination Plain Text Naples, Italy Proposal Plain Text Based on the comprehensive analysis of your friends' suggestions, **Naples** emerges as the ideal city for your vacation in Italy. Here's why Naples stands out as the best choice, offering an exceptional mix of excellent food, beautiful seaside experiences, and a vibrant salsa and bachata dance scene: ### **1. Vibrant Dance Scene** - **Dance Venues:** Naples boasts numerous venues and events dedicated to salsa and bachata, ensuring that you can immerse yourself in lively dance nights regularly. - **Passionate Culture:** The city's passionate and energetic atmosphere enhances the overall dance experience, making it a hotspot for Latin dance enthusiasts. ### **2. Culinary Excellence** - **Authentic Neapolitan Pizza:** As the birthplace of pizza, Naples offers some of the best and most authentic pizzerias in the world. - **Fresh Seafood:** Being a coastal city, Naples provides access to a wide variety of fresh seafood dishes, enhancing your culinary adventures. - **Delicious Pastries:** Don't miss out on local specialties like **sfogliatella**, a renowned Neapolitan pastry that is a must-try for any foodie. ### **3. Stunning Seaside Location** - **Bay of Naples:** Enjoy breathtaking views and activities along the Bay of Naples, including boat tours and picturesque sunsets. - **Proximity to Amalfi Coast:** Naples serves as a gateway to the famous Amalfi Coast, allowing you to explore stunning coastal towns like Amalfi, Positano, and Sorrento with ease. - **Beautiful Beaches:** Relax on the city's beautiful beaches or take short trips to nearby seaside destinations for a perfect blend of relaxation and exploration. ### **4. Cultural Richness** - **Historical Sites:** Explore Naples' rich history through its numerous museums, historic sites, and UNESCO World Heritage landmarks such as the Historic Centre of Naples. - **Vibrant Nightlife:** Beyond dancing, Naples offers a lively nightlife scene with a variety of bars, clubs, and entertainment options to suit all tastes. ### **5. Accessibility and Convenience** - **Transportation Hub:** Naples is well-connected by air, rail, and road, making it easy to travel to other parts of Italy and beyond. - **Accommodation Options:** From luxury hotels to charming boutique accommodations, Naples offers a wide range of lodging options to fit your preferences and budget. ### **Conclusion** Naples perfectly balances a thriving dance scene, exceptional culinary offerings, and beautiful seaside attractions. Its unique blend of culture, history, and vibrant nightlife makes it the best city in Italy to fulfill your desires for travel, good food, and lively dance experiences. Whether you're dancing the night away, savoring authentic pizza by the sea, or exploring nearby coastal gems, Naples promises an unforgettable vacation. ### **Additional Recommendations** - **Day Trips:** Consider visiting nearby attractions such as Pompeii, the Isle of Capri, and the stunning Amalfi Coast to enrich your travel experience. - **Local Experiences:** Engage with locals in dance classes or attend festivals to dive deeper into Naples' vibrant cultural scene. Enjoy your trip to Italy, and may Naples provide you with the perfect blend of everything you're looking for! Cost Plain Text To estimate the cost of traveling from Oslo, Norway, to Naples, Italy, for two adults and two kids, we need to consider several key components of the trip: flights, accommodations, local transportation, food, and activities. Here's a breakdown of potential costs: 1. **Flights**: - Round-trip flights from Oslo to Naples typically range from $100 to $300 per person, depending on the time of booking, the season, and the airline. Budget airlines might offer lower prices, while full-service carriers could be on the higher end. - For a family of four, the cost could range from $400 to $1,200. 2. **Accommodations**: - Hotels in Naples can vary significantly. Expect to pay approximately $70 to $150 per night for a mid-range hotel room that accommodates a family. Vacation rentals might offer more flexibility and potentially lower costs. - For a typical 5-night stay, this would range from $350 to $750. 3. **Local Transportation**: - Public transportation in Naples (buses, metro, trams) is affordable, and daily tickets cost around $4 per person. - Assume about $50 to $100 for the family's local transport for the entire trip, depending on usage. 4. **Food**: - Dining costs are highly variable. A budget for meals might be around $10-$20 per person per meal at casual restaurants, while dining at mid-range restaurants could cost $20-$40 per person. - A family of four could expect to spend around $50 to $100 per day, reaching a total of $250 to $500 for five days. 5. **Activities**: - Entry fees for attractions can vary. Some museums and archaeological sites charge around $10 to $20 per adult, with discounts for children. - Budget around $100 to $200 for family activities and entrance fees. 6. **Miscellaneous**: - Always allow a little extra for souvenirs, snacks, and unexpected expenses. A typical buffer might be $100 to $200. **Estimated Total Cost**: - **Low-end estimate**: $1,250 - **High-end estimate**: $2,950 These are general estimates and actual costs can vary based on when you travel, how far in advance you book, and your personal preferences for accommodation and activities. For the most accurate assessment, consider reaching out to airlines for current flight prices, hotels for room rates, and looking into specific attractions you wish to visit. That was a lot, and this is only the output of the two “reasoning” models! But the result is quite interesting. Naples is on my bucket list, and I am curious to see if the agent is correct! Let’s also check the intermediate results to see how it reached this conclusion, which seems reasonable to me. Intermediate Outputs If you are curious, there are intermediate results. Food Plain Text As a foodie exploring Italy, you're in for a treat, as the country boasts a rich culinary heritage with regional specialties. Here's a list of the top 10 cities in Italy renowned for their food: 1. **Bologna** - Often referred to as the gastronomic heart of Italy, Bologna is famous for its rich Bolognese sauce, tasty mortadella, and fresh tagliatelle. 2. **Naples** - The birthplace of pizza, Naples offers authentic Neapolitan pizza, as well as delicious seafood and pastries like sfogliatella. 3. **Florence** - Known for its Florentine steak, ribollita (a hearty bread and vegetable soup), and delicious wines from the surrounding Tuscany region. 4. **Rome** - Enjoy classic Roman dishes such as carbonara, cacio e pepe, and Roman-style artichokes in the bustling capital city. 5. **Milan** - A city that blends tradition and innovation, Milan offers risotto alla milanese, ossobuco, and an array of high-end dining experiences. 6. **Turin** - Known for its chocolate and coffee culture, as well as traditional dishes like bagna cauda and agnolotti. 7. **Palermo** - Sample the vibrant street food scene with arancini, panelle, and sfincione, as well as fresh local seafood in this Sicilian capital. 8. **Venice** - Famous for its seafood risotto, sarde in saor (sweet and sour sardines), and cicchetti (Venetian tapas) to enjoy with a glass of prosecco. 9. **Parma** - Home to the famous Parmigiano-Reggiano cheese and prosciutto di Parma, it’s a haven for lovers of cured meats and cheeses. 10. **Genoa** - Known for its pesto Genovese, focaccia, and variety of fresh seafood dishes, Genoa offers a unique taste of Ligurian cuisine. Each of these cities offers a distinct culinary experience influenced by local traditions and ingredients, making them must-visit destinations for any food enthusiast exploring Italy. Sea Plain Text Italy is renowned for its stunning coastline and beautiful seaside cities. Here are ten top cities and regions perfect for a sea vacation: 1. **Amalfi** - Nestled in the famous Amalfi Coast, this city is known for its dramatic cliffs, azure waters, and charming coastal villages. 2. **Positano** - Also on the Amalfi Coast, Positano is famous for its colorful buildings, steep streets, and picturesque pebble beachfronts. 3. **Sorrento** - Offering incredible views of the Bay of Naples, Sorrento serves as a gateway to the Amalfi Coast and provides a relaxing seaside atmosphere. 4. **Capri** - The island of Capri is known for its rugged landscape, upscale hotels, and the famous Blue Grotto, a spectacular sea cave. 5. **Portofino** - This quaint fishing village on the Italian Riviera is known for its picturesque harbor, pastel-colored houses, and luxurious coastal surroundings. 6. **Cinque Terre** - Comprising five stunning villages along the Ligurian coast, Cinque Terre is a UNESCO World Heritage site known for its dramatic seaside and hiking trails. 7. **Taormina** - Situated on a hill on the east coast of Sicily, Taormina offers sweeping views of the Ionian Sea and beautiful beaches like Isola Bella. 8. **Rimini** - Located on the Adriatic coast, Rimini is known for its long sandy beaches and vibrant nightlife, making it a favorite for beach-goers and party enthusiasts. 9. **Alghero** - A city on the northwest coast of Sardinia, Alghero is famous for its medieval architecture, stunning beaches, and Catalan culture. 10. **Lerici** - Near the Ligurian Sea, Lerici is part of the stunning Gulf of Poets and is known for its beautiful bay, historic castle, and crystal-clear waters. Each of these destinations offers a unique blend of beautiful beaches, cultural sites, and local cuisine, making Italy a fantastic choice for a sea vacation. Activity Plain Text Italy has a vibrant dance scene with many cities offering great opportunities to enjoy salsa and bachata. Here are ten cities where you can indulge in these lively dance styles: 1. **Rome** - The capital city has a bustling dance scene with numerous salsa clubs and events happening regularly. 2. **Milan** - Known for its nightlife, Milan offers various dance clubs and events catering to salsa and bachata enthusiasts. 3. **Florence** - A cultural hub, Florence has several dance studios and clubs where you can enjoy Latin dances. 4. **Naples** - Known for its passionate culture, Naples offers several venues and events for salsa and bachata lovers. 5. **Turin** - This northern city has a growing salsa community with events and social dances. 6. **Bologna** - Known for its lively student population, Bologna has a number of dance clubs and events for salsa and bachata. 7. **Venice** - While famous for its romantic canals, Venice also hosts various dance events throughout the year. 8. **Palermo** - In Sicily, Palermo has a vibrant Latin dance scene reflecting the island's festive culture. 9. **Verona** - Known for its romantic setting, Verona has several dance studios and clubs for salsa and bachata. 10. **Bari** - This coastal city in the south offers dance festivals and clubs perfect for salsa and bachata enthusiasts. These cities offer a mix of cultural experiences and lively dance floors, ensuring you can enjoy salsa and bachata across Italy. Interestingly enough, Naples does not top any of the lists, though the first four cities in the sea list are all close to Naples. Licensing Details Before closing the article, just two words on the license of Fibry. Fibry is no longer distributed as a pure MIT license. The main difference now is that if you want to build a system to generate code at scale for third parties (like a software engineer agent), you need a commercial license. Also, it is forbidden to include it in any datasets to train systems to generate code (e.g., ChatGPT should not be trained on the source code of Fibry). Anything else, you are good to go. I can provide commercial support and develop features on demand. Conclusion I hope you had fun and could get an idea of how to use Fibry to write AI agents. If you think that a multi-agent system needs to be distributed and run on multiple nodes, Fibry has got you covered! While we’ll save the details for another article, it’s worth noting that setting up Fibry actors in a distributed system is straightforward, and your agents are already actors: when you call process() or processAsync(), a message is sent to the underlying actor. In Fibry, sending and receiving messages over the network is abstracted away, so you don’t even need to modify your agent logic to enable distribution. This makes Fibry uniquely simple for scaling across nodes without rewriting core logic. Happy coding!
After working on a new online Spring Data Neo4j course, I learned a couple more things about updating an entity. The course required a different set of scenarios than outlined in my previous SDN update blog post, so I wanted to cover those scenarios, as well. Spring save() Method First up is the out-of-the-box save() method that is provided by Spring as a default. This method takes an input of the entity object you want to save to the database. Here is an example of what this looks like with a popular movie data set as the model. MovieDomain Class Java @Node public class Movie { @Id private String movieId; private String title; private String plot; private String poster; private String url; private String released; private Long imdbVotes; @Relationship(value = "ACTED_IN", direction = Relationship.Direction.INCOMING) private List<Role> actors; } MovieController Class Java @RestController @RequestMapping("/movies") public class MovieController { //inject repository + constructor @PostMapping("/save") Movie save(@RequestBody Movie movie) { return movieRepo.save(movie); } } This works well to save a new movie, as you can provide a subset of properties or all properties, as long as the id is present, and anything you don't provide will be set to null. However, the issue arises when you need to update an existing entity because it means any attributes you do not provide will be overwritten to null. To better understand how this works, let's see it in action by saving a movie, and then trying to update it. Movie Request Object JSON { "movieId": "9876", "title": "MyMovie" } Save Movie Object Shell % http ":8080/movies/save" @src/main/resources/movie.json { "actors": null, "imdbId": null, "imdbVotes": null, "movieId": "9876", "plot": null, "poster": null, "title": "MyMovie", "url": null, "year": null } Now, let's use the same method to try to update the movie with a year property. Updated Movie Object JSON { "movieId": "9876", "year": 2018 } Save Updated Movie Object Shell % http ":8080/movies/save" @src/main/resources/movieUpdated.json { "actors": null, "imdbId": null, "imdbVotes": null, "movieId": "9876", "plot": null, "poster": null, "title": null, "url": null, "year": 2018 } In the output above, you can see that the title property is null, and the year property is populated. This is because the title is not specified in the updated JSON object, so it is overwritten to null. This may not be too big of an effort if you have only a few attributes in your domain class, although I would still find it frustrating to include all properties for only updating one or two properties. In that case, I would need to resort to the POJO method outlined in my previous SDN update blog post where you pull the database entity, set each property, and then save the entity. The added business logic seems like an unnecessary maintenance headache. What if you wanted to preserve what was already there without providing all the properties defined in the domain class to the request? In these scenarios, there are a couple of other options at your disposal, though neither allows dynamic updates to random properties per request. Patch Year The first option is that you don't have to set all properties if you use a PATCH method and only set the values you want to update. Here is an example. MovieController Class Java @RestController @RequestMapping("/movies") public class MovieController { //inject repository + constructor @PatchMapping("/patchYear") Movie patchYear(@RequestBody Movie moviePatch) { Movie existingMovie = movieRepository.findById(moviePatch.getMovieId()).get(); existingMovie.setYear(moviePatch.getYear()); return movieRepository.save(existingMovie); } } PatchYear Request Object JSON { "movieId": "9876", "year": 2024 } Patch Movie Year Shell % http PATCH ":8080/movies/patchYear" @src/main/resources/moviePatch.json { "budget": null, "countries": null, "imdbId": null, "imdbRating": null, "imdbVotes": null, "languages": null, "movieId": "9876", "plot": null, "poster": null, "released": null, "revenue": null, "runtime": null, "title": "MyMovie", "tmdbId": null, "url": null, "year": 2024 } This allows you to set specific values without overwriting other property values to null. You also don't need to set all the values in the movie object programmatically. If you modified the initial save() method to just include the setYear() line, it would still overwrite other values. This approach prevents that, although you still have to call setProperty() for each field you want to update. Note: For this approach to work, your domain entity must be a class (not a record) because records are immutable, which means you cannot change (or set/update) fields on the entity instance. For immutable objects, you have to create a new instance of the object and copy property values to the new object before saving. You can avoid setting each property on an object and retain existing values with a couple of options covered next. Custom Cypher One of the more flexible options is to use custom Cypher. For this, you would write a Cypher statement that sets the new values to the properties you want to update. You can even add/set properties that do not exist on the application's domain class. The negative is that you would need to make changes to the application (Cypher statement) if you wanted to update different properties, so it is not fully dynamic. The example below uses the same movie domain but adds a Cypher statement and method to the repository interface to update the year property without overwriting the title. MovieRepository Interface Java interface MovieRepository extends Neo4jRepository<Movie, String> { @Query("MATCH (m:Movie {movieId: $movieId}) " + "SET m.year = toInteger($year) " + "RETURN m;") Movie updateYear(String movieId, Long year); } MovieController Class Java @RestController @RequestMapping("/movies") public class MovieController { //inject repository + constructor @PatchMapping("/updateYear") Movie patchYear(@RequestParam String movieId, @RequestParam Long year) { return movieRepository.updateYear(movieId, year); } } Then, the following request calls the method and updates the movie's year property. Update Movie Year Shell % http PATCH ":8080/movies/updateYear?movieId=9876&year=2018" { "actors": [], "imdbId": null, "imdbVotes": null, "movieId": "9876", "plot": null, "poster": null, "title": "MyMovie", "url": null, "year": 2018 } It worked! The movie's title remained the same (not overwritten to null), and a value was saved for the year property. This ad hoc Cypher approach could work well when values or property updates occur somewhat frequently, as updating the Cypher statement makes updates flexible. You could also make the incoming property generic (value) and pass in any value (or multiple values) and set whichever properties you'd like by changing the Cypher. While still not completely dynamic, this option is probably the most flexible and dynamic of the list. A custom Cypher approach might work well when you need to update certain properties, but if you have a subset of properties that operate together, another option is to create a projection of the domain class. Projections To provide a consistent set of values for updates and leave other properties as-is, projections are probably the nicest option I've found so far. This approach still requires setting consistent properties (like with custom Cypher) but avoids overwriting consistent values by creating a "view" of the larger entity, only working with those values and leaving other field values alone. Note: There are two types of projections — interface and DTO. Interface projections are immutable, which means you cannot update values, but have to create a new object and copy existing values over. DTO objects, then, are more straightforward when dealing with update operations. For this reason, the examples use DTO-based projections. There are two different ways to save a projection — 1. send the projection (a subset of properties) and save it into the full domain entity; 2. send a full domain object but only save the projection fields. Really, the difference is the incoming request object, whether you have a smaller set or larger set and only want to save those values. Let's see how this operates. Projection as Movie The first example sends a projection object (subset of the full domain object's properties) and saves the trimmed object as the full movie entity. We have defined a projection that only includes the movieId and plot properties of a movie. MovieDTOProjection Class Java public class MovieDTOProjection { private String movieId; private String plot; public String getMovieId() { return movieId; } public String getPlot() { return plot; } public void setPlot(String plot) { this.plot = plot; } } MovieController Class Method Java @RestController @RequestMapping("/movies") public class MovieController { //inject repository + constructor //Only updates properties in projection @PatchMapping("/projectionAsMovie") MovieDTOProjection saveProjectionAsMovie(@RequestBody MovieDTOProjection movieDTO) { return neo4jTemplate.save(Movie.class).one(movieDTO); } } ProjectionAsMovie Object (Request Object) JSON { "movieId": "9876", "plot": "Here is the plot." } Update Movie With a Projection Object Shell % http PATCH ":8080/movies/projectionAsMovie" @src/main/resources/projectionAsMovie.json { "movieId": "9876", "plot": "Here is the plot." } Full Database Entity JSON { "plot": "Here is the plot.", "year": 2024, "movieId": "9876", "title": "MyMovie" } The request successfully updated the entity with the new plot value ("Here is the plot.") without overwriting the title or year properties on the existing entity! The method in the controller class takes the projection object input and saves it as a Movie class entity. This approach is helpful when you only want to send certain properties over the wire and not the full object. You can set any fields you wish to update in the projection and only send those values in the request. Movie Entity as Projection This type of projection only saves a defined subset of properties out of a potentially larger or variable object. The save will only update the fields included in the projection and ignore anything else that might be in the request object. The example below uses the same MovieDTOProjection class as the above example. Then, we only need a new method in the controller to save the projection. MovieController Class Method Java @RestController @RequestMapping("/movies") public class MovieController { //inject repository + constructor //Only updates properties in projection (ignores other values) @PatchMapping("/movieAsProjection") MovieDTOProjection saveMovieAsProjection(@RequestBody Movie movie) { return neo4jTemplate.saveAs(movie, MovieDTOProjection.class); } } MovieAsProjection Object JSON { "movieId": "9876", "title": "TestTitle", "plot": "Some plot cliche here.", "year": 2025 } Send Movie Object (Only Save Projection Values) Shell % http PATCH ":8080/movies/movieAsProjection" @src/main/resources/movieAsProjection.json { "movieId": "9876", "plot": "Some plot cliche here." } Full Database Entity JSON { "plot": "Some plot cliche here.", "year": 2024, "movieId": "9876", "title": "MyMovie" } This also worked! The controller method accepts a Movie request object as input and saves it as the projection entity, which only retains the subset of values defined in the projection and ignores the rest. This approach would be helpful if you have request objects with varying fields of a Movie entity, but only want to update a certain subset each time. In terms of the incoming request object, this option seems to be the most flexible in allowing you to provide all kinds of fields as input, and it will only save the relevant ones defined in the projection. Wrapping Up! In this post, we delved a bit deeper into updating entities using Spring Data and Neo4j. A previous blog post on the topic outlined some examples for that use case at the time (microservices), but I learned about a few more options after working on the GraphAcademy Spring Data Neo4j course. These options included custom Cypher statements and projections. There are still other pieces of the puzzle to explore and integrate, such as optimistic locking (with the @Version field) and custom repository implementations for extending repositories (e.g., to combine Neo4jRepository<>, Neo4jTemplate, and/or Neo4jClient levels of abstraction), but we will save those for a future blog post. Happy coding! Resources Code: sdn-update-entity-round2Blog post: previous SDN update articleGraphacademy course (free, online, self-paced): Spring Data Neo4jDocumentation: SDN projection persistenceDocumentation: SDN custom repository implementations
In this blog, we will explore what chat completion models can and cannot do and then see how Assistants API addresses those limitations. We will also focus on threads and messages — how to create them, list them, retrieve them, modify them, and delete them. Additionally, we will add some Python code snippets and show possible outputs based on the script language. Limitations of Chat Completion Models No Memory Chat completion models do not have a memory concept. For example, if you ask: “What’s the capital of Japan?” The model might say: “The capital of Japan is Tokyo.” But when you ask again: “Tell me something about that city.” It often responds with: “I’m sorry but you didn’t specify which city you are referring to.” It does not understand what was discussed previously. That’s the main issue: there is no memory concept in chat completions. Poor at Computational Tasks Chat completion models are really bad at direct computational tasks. For instance, if you want to reverse the string “openaichatgpt,” it may generate the wrong output, like inserting extra characters or missing some letters. No Direct File Handling In chat completions, there is no way to process text files or Word documents directly. You have to convert those files to text, do chunking (divide documents into smaller chunks), create embeddings, and do vector searches yourself. Only then do you pass some relevant text chunks to the model as context. Synchronous Only Chat completion models are not asynchronous. You must ask a question and wait for it to finish. You cannot do something else while it’s processing without extra workarounds. Capabilities of the Assistants API Context Support With Threads In Assistants API, you can create a thread for each user. A thread is like a chat container where you can add many messages. It persists the conversation, so when the user logs in again, you can pass the same thread ID to retrieve what was discussed previously. This is very helpful. Code Interpreter There is also a code interpreter. Whenever you ask for some computational task, it runs Python code. It then uses that answer to expand or explain. This makes it very helpful for reversing strings, finding dates, or any Python-based operations. Retrieval With Files The Assistants API has retrieval support, letting you upload files and ask questions based on those files. The system handles the vector search process and then uses relevant chunks as context. You can upload up to 20 files in Assistants as context. This is very helpful for referencing company documents, reports, or data sets. Function Calling Function calling allows the model to tell you what function to call and what arguments to pass, so that you can get external data (like weather or sales from your own database). It does not call the function automatically; it indicates which function to call and with what parameters, and then you handle that externally. Asynchronous Workflows The Assistants API is asynchronous. You can run a request, and you don’t have to wait for it immediately. You can keep checking if it’s done after a few seconds. This is very helpful if you have multiple tasks or want to do other things in parallel. Focusing on Threads and Messages A thread is essentially a container that holds all messages in a conversation. OpenAI recommends creating one thread per user as soon as they start using your product. This thread can store any number of messages, so you do not have to manually manage the context window. Unlimited messages. You can add as many user queries and assistant responses as you want.Automatic context handling. The system uses truncation if the conversation grows beyond token limits.Metadata storage. You can store additional data in the thread’s metadata (for example, user feedback or premium status). Below are code snippets to demonstrate how to create, retrieve, modify, and delete threads. 1. Creating an Assistant First, you can create an assistant with instructions and tools. For example: Python from openai import OpenAI client = OpenAI() file_input = client.files.create(file=open("Location/to/the/path", "rb"), purpose = "assistants") file_input.model_dump() Python assistant = client.beta.assistants.create( name="data_science_tutor", instructions="This assistant is a data science tutor.", tools=[{"type":"code_interpreter", {"type":"retrieval"}], model="gpt-4-1106-preview", file_ids=[file_input.id] ) print(assistant.model_dump()) 2. Creating Threads A thread is like a container that holds the conversation. We can create one thread per user. Python thread = client.beta.threads.create() print(thread.model_dump()) id – a unique identifier that starts with thr-object – always "thread"metadata – an empty dictionary by default Why Create Separate Threads? OpenAI recommends creating one thread per user as soon as they start using your product. This structure ensures that the conversation context remains isolated for each user. 3. Retrieving a Thread Python retrieved_thread = client.beta.threads.retrieve(thread_id=thread.id) print(retrieved_thread.model_dump()) This returns a JSON object similar to what you get when you create a thread, including the id, object, and metadata fields. 4. Modifying a Thread You can update the thread’s metadata to store important flags or notes relevant to your application. For instance, you might track if the user is premium or if the conversation has been reviewed by a manager. Python updated_thread = client.beta.threads.update( thread_id=thread.id, metadata={"modified_today": True, "user_is_premium": True} ) print(updated_thread.model_dump()) modified_today – a custom Boolean to note whether you changed the thread todayuser_is_premium – a Boolean flag for user account tierconversation_topic – a string that labels this thread’s main subject Further Metadata Examples {"language_preference": "English"} – if the user prefers answers in English or another language{"escalated": true} – if the thread needs special attention from a support team{"feedback_rating": 4.5} – if you collect a rating for the conversation 5. Deleting a Thread When you no longer need a thread, or if a user deletes their account, you can remove the entire conversation container: Python delete_response = client.beta.threads.delete(thread_id=thread.id) print(delete_response.model_dump()) Once deleted, you can no longer retrieve this thread or any messages it contained. Working With Messages Previously, we focused on threads — the containers that hold conversations in the Assistants API. Now, let’s explore messages, which are the individual pieces of content (questions, responses, or system notes) you add to a thread. We’ll walk through creating messages, attaching files, listing and retrieving messages, and updating message metadata. We’ll also show Python code snippets illustrating these steps. Messages and Their Role in Threads What Are Messages? Messages are mostly text (like user queries or assistant answers), but they can also include file references. Each thread can have many messages, and every message is stored with an ID, a role (for example, "user" or "assistant"), optional file attachments, and other metadata. Opposite Index Order Unlike chat completions, where the first message in the list is the earliest, here, the first message you see in the array is actually the most recent. So, index 0 corresponds to the newest message in the thread. Annotations and File Attachments Messages can include annotations, for instance, if a retrieval step references certain files. When using a code interpreter, any new files generated may also appear as part of the message annotations. Create a Message in a Thread Messages are added to a thread. Each message can be a user message or an assistant message. Messages can also contain file references. Before adding messages, we need a thread. If you do not already have one: Python # Create a new thread new_thread = client.beta.threads.create() print(thread.model_dump()) # Shows the thread's detailspython Python # Create a new message in the thread message = client.beta.threads.messages.create( thread_id=thread.id, role="user", content="ELI5: What is a neural network?", file_ids=[file_input.id] # Passing one or more file IDs ) print(message.model_dump()) Here, you can see: Message ID – unique identifier starting with msgRole – user, indicating this is a user inputFile attachments – the file_ids list includes any referenced filesAnnotations – empty at creation, but can include details like file citations if retrieval is involvedMetadata – a placeholder for storing additional key-value pairs List Messages in a Thread To list messages in a thread, use the list method. The limit parameter determines how many recent messages to retrieve. Now, let’s try to list all the messages: You will see only the most recent messages. For instance, if we’ve added just one message, the output will look like: Python messages_list = client.beta.threads.messages.list( thread_id=thread.id, limit=5 ) for msg in messages_list.data: print(msg.id, msg.content) If there are multiple messages, the system works like a linked list: The first ID points to the newest message.The last ID points to the earliest message. Retrieve a Specific Message Python retrieved_msg = client.beta.threads.messages.retrieve( thread_id=new_thread.id, message_id=message.id ) print(retrieved_msg.model_dump()) Retrieve a Message File Now, let’s retrieve a message file: This provides the file’s metadata, including its creation timestamp. Python files_in_msg = client.beta.threads.messages.files.list( thread_id=new_thread.id, message_id=message.id ) print(files_in_msg.model_dump()) Modify a Message Python updated_msg = client.beta.threads.messages.update( thread_id=new_thread.id, message_id=message.id, metadata={"added_note": "Revised content"} ) print(updated_msg.model_dump()) Delete a Message Python deleted_msg = client.beta.threads.messages.delete( thread_id=new_thread.id, message_id=message.id ) print(deleted_msg.model_dump()) We have seen that chat completion models have no memory concept, are bad at computational tasks, cannot process files directly, and are not asynchronous. Meanwhile, Assistants API has context support with threads, code interpreter for computational tasks, retrieval for files, function calling for external data, and it also supports asynchronous usage. In this blog, we focused on how to create, list, retrieve, modify, and delete threads and messages. We also saw how to handle file references within messages. In the next session, we will learn more about runs, which connect threads and assistants to get actual outputs from the model. I hope this is helpful. Thank you for reading! Let’s connect on LinkedIn! Further Reading Where did multi-agent systems come from?Summarising Large Documents with GPT-4oHow does LlamaIndex compare to LangChain in terms of ease of use for beginnersPre-training vs. Fine-tuning [With code implementation]Costs of Hosting Open Source LLMs vs Closed Sourced (OpenAI)Embeddings: The Back Bone of LLMsHow to Use a Fine-Tuned Language Model for Summarization
SQL Server Dynamic Data Masking is a feature that allows you to obscure sensitive data from non-privileged accounts, improving data security and compliance. Rather than showing credit card numbers, passwords, or personal identifiers in cleartext, you can define masking rules at the column level so that specific users see only masked values. In contrast, others with elevated permissions see the actual data. When to Use Dynamic Data Masking Lower environments (development, QA). Typically, developers and testers do not need access to actual sensitive information. Masking ensures that they can work with realistic datasets without risking exposure to PII.Third-party access. When sharing data with external consultants or analytics vendors, masked data prevents inadvertent or malicious disclosure of sensitive content.Regulatory compliance. For environments where regulations like GDPR, HIPAA, or PCI-DSS apply, dynamic masking helps ensure only authorized personnel can view sensitive data in cleartext Prerequisites SQL server version. Dynamic data masking is available in SQL Server 2016 and later.Permissions and roles. To create or modify masking rules, you must have the ALTER ANY MASK and ALTER permissions on the table. End-users who only have SELECT permissions on the table or view will automatically be served masked data if they do not have UNMASK permission.Assessment of sensitive fields. Identify which columns contain PII or sensitive data. Typical candidates: Email addressesPhone numbersNational identifiers (e.g., SSN)Credit card numbersPasswords or security answers How to Implement Dynamic Data Masking 1. Identify Columns to Mask Review each column and decide which requires masking using the query below: MS SQL SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Customers' ORDER BY TABLE_NAME, COLUMN_NAME; 2. Choose a Masking Function SQL Server provides a few built-in masking functions: Default() – masks the entire value using a default value depending on the data typeEmail() – masks the email format (e.g., xxx@example.com)Partial (prefix, padding, suffix) – allows partial masking of a string. For example, it shows the first character, masks the middle, and shows the last character.Random ([start range], [end range]) – for numeric columns, a random number in the specified range is returned. Example Masking Scenarios Name fields (e.g., FirstName, LastName). Use partial() to show maybe the first letter and mask the rest.Email address. Use email() for a consistent masked pattern.Credit card number. Use partial() to show only the last four digits.Password columns. Use default() to mask fully. 3. Apply Masking to the Table For example, consider a Customers table with columns: FirstName, LastName, Email, CreditCardNumber, and Password. Below are some sample queries: MS SQL -- Mask the first name to show only the first letter ALTER TABLE Customers ALTER COLUMN FirstName ADD MASKED WITH (FUNCTION = 'partial(1, "****", 0)'); -- Mask the last name similarly ALTER TABLE Customers ALTER COLUMN LastName ADD MASKED WITH (FUNCTION = 'partial(1, "*****", 0)'); -- Mask the email using built-in email masking ALTER TABLE Customers ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()'); -- Mask credit card number to show only the last 4 digits ALTER TABLE Customers ALTER COLUMN CreditCardNumber ADD MASKED WITH (FUNCTION = 'partial(0,"****-****-****-",4)'); -- Mask the password fully ALTER TABLE Customers ALTER COLUMN Password ADD MASKED WITH (FUNCTION = 'default()'); Important: You must be a member of the db_owner role or have the ALTER ANY MASK permission in the database. 4. Create a Non-Privileged User or Role to Test Masking Use the below queries: MS SQL CREATE USER MaskedUser WITHOUT LOGIN; GRANT SELECT ON Customers TO MaskedUser; When MaskedUser queries the table: EXECUTE AS USER = 'MaskedUser'; SELECT FirstName, LastName, Email, CreditCardNumber, Password FROM Customers; REVERT; They will see masked data. If an administrator with the UNMASK permission runs the same query, they will see the real data. 5. Monitoring and Verification Data audits. Regularly audit queries and logins to ensure no unauthorized UNMASK permissions are granted.Test masking patterns. Confirm that the masked output meets your compliance and business requirements. For instance, ensure the displayed format (like ****-****-****-1234 for a credit card) is correct.Documentation. Maintain a data dictionary or schema documentation that notes which columns are masked and how so that team members understand what they see in downstream environments. Example Result Original data (Admins with UNMASK): FirstNameLastNameEmailCreditCardNumberPasswordAliceNadellaalice.n@example.com4111-1111-1111-1231MySecretPass%JohnYesujohn.y@example.com5555-6666-7777-8899Password123$ View for Non-Privileged Users: FirstNameLastNameEmailCreditCardNumberPasswordA****S*****xxx@xxxx.com****-****-****-1234****J****D*****xxx@xxxx.com****-****-****-8888**** Conclusion Implementing Dynamic Data Masking in SQL Server is one meaningful step towards what many people have begun to call 'privacy-first' architecture. In any case, the implementation will align with basic data protection principles, such as under GDPR and CCPA, allowing only correctly authorized users to see sensitive data in full view. In contrast, other users get values either masked or partially to decrease the possibility of unauthorized disclosure of personal information. Data Minimization and Access Control GDPR and CCPA are based on data minimization, meaning they only provide the data necessary for a particular task. Dynamic masking ensures that you show only the minimal, masked version of sensitive data to non-privileged roles, thus adhering to these regulations. Improved Protection and Exposure Reduction It minimizes the risk of personal data exposure by storing sensitive data in masked form within the databases while processing. Should unauthorized access or a data breach at the application/reporting layer occur, the data already shown is masked. Therefore, such an event would have minimal impact on the subjects. Audit and Compliance Readiness Well-documented masking rules and role-based permissions support the accountability principle of GDPR and the transparency requirements of CCPA. Auditors can easily verify that your organization has technical measures to safeguard personal information, helping demonstrate compliance and due diligence. Ease of Implementation in Development and Testing For lower environments, where developers and testers often need "realistic" data, dynamic masking provides a systematic way to ensure personal information is never exposed. This approach helps maintain privacy protections throughout the data lifecycle. Scalability and Consistency Because data masking is dynamic and at the database layer, it scales to multiple applications, services, and analytic tools. This uniformity supports clearly outlined compliance policies and reduces the chance of policy drift or mistakes in bespoke masking logic at different codebases. Incorporating dynamic data masking into your general privacy-by-design strategy allows you to protect data subjects' privacy interests while lowering the risks of regulatory fines and engendering better customer trust. This fits within the GDPR's focus on privacy by design and by default, and the CCPA demands reasonable security measures to safeguard consumer data.
Are you looking for a user-friendly, self-hosted AI interface designed to operate entirely offline? Look no further; Open WebUI might be what you are looking for. In this blog, you will dive into some nice features of Open WebUI. Enjoy! Introduction Quite some inference engines exist which can run locally, for example, Ollama, LMStudio, LocalAI, Jan, and many others. Some of them also come with a Graphical User Interface (GUI). However, these run locally on the same machine as the inference engine. What if you would like to offer a ChatGPT-like user interface within your company that runs entirely offline? The inference engine runs on dedicated machines with GPUs (on-premise or in a private cloud), and the GUI is a web application available to your users. The advantage of this setup is that all your data does not leave your company and is not shared with any cloud vendor. The privacy of your data is secured with this setup. The disadvantage is that you might not be able to run large models and that you need to make a trade-off between quality and performance. However, Exo and JLama are initiatives that are working on AI clustering, which will make it possible to run large models locally. It is just a matter of time before this functionality will be available. In the meanwhile, Open WebUI provides you with a web application that offers quite some functionality to run your AI solution entirely offline. Let’s explore some of its features in this blog. Check out the official documentation of Open WebUI, which is not complete but offers good information about its features. Sources used in this blog are available on GitHub. Prerequisites Prerequisites for reading this blog are: Basic knowledge of Docker and Docker Compose;Basic knowledge of large language models (LLMs);Basic knowledge of retrieval-augmented generation (RAG). More info can be found in a previous blog. 3. Installation Several methods are available for installing Open WebUI. In this section, the setup with a locally running Ollama instance is described together with Open WebUI running in a Docker container. For ease of use, the Docker command is converted to a compose.yaml file. YAML version: '3' services: open-webui: image: ghcr.io/open-webui/open-webui:main container_name: open-webui ports: - "3000:8080" volumes: - open-webui:/app/backend/data volumes: open-webui: Start the container from where the compose file is located. Shell $ docker compose up After startup, Open WebUI is accessible via http://localhost:3000/. Create an admin account. After a successful login, the webpage shows a blank screen. After approximately 5 minutes, an error occurs in the logs. Plain Text open-webui | ERROR [open_webui.routers.ollama] Connection error: Cannot connect to host host.docker.internal:11434 ssl:default [Connect call failed ('172.17.0.1', 11434)] After this error, the release notes were shown. However, it was not possible to select a model, although models are available in the locally running Ollama instance. Shut down the container with CTRL+C or by entering the command: Shell $ docker compose down Apparently, Open WebUI is not able to access the running Ollama instance on the host. Therefore, you can bind the container to your host network, as described here. The compose file becomes the following. YAML version: '3' services: open-webui: image: ghcr.io/open-webui/open-webui:main container_name: open-webui network_mode: host volumes: - open-webui:/app/backend/data environment: - OLLAMA_BASE_URL=http://localhost:11434 volumes: open-webui: Start the container again. Shell $ docker compose up Note that Open WebUI is now accessible via http://localhost:8080/. My First Prompt Ollama already contains some models. If you have not installed any, you can install llama3.2:3b which is a small and good model to start with. Shell $ ollama pull llama3.2:3b Now that the connection with Ollama is working, you will see all available models in Open WebUI. As a first prompt, you ask the model to tell a joke. And the model returns a joke. This works quite similarly to any other AI service, but the difference is here that everything is running locally. No data is sent outside of your laptop! Knowledge A very interesting feature is the knowledge feature. This allows you to add your own documents as context to the model. It is, in fact, retrieval-augmented generation (RAG). You can add your documents by clicking the workspace in the left menu. This will show you some tabs under the Knowledge tab. In a previous blog, RAG was implemented and evaluated by using three markdown documents containing data in table format about songs and albums of Bruce Springsteen. Let’s add these documents. Click the +-sign at the right to create a new knowledge base. Drag and drop each file to the knowledge base. Be patient and add the next file while the processing of the current one is finished. Otherwise, the browser might crash. Now that the knowledge base is created, let’s evaluate the responses based on the following questions and answers. on which album was “adam raised a cain” originally released? The answer is “Darkness on the Edge of Town”.what is the highest chart position of “Greetings from Asbury Park, N.J.” in the US? This answer is #60.what is the highest chart position of the album “tracks” in canada? The album did not have a chart position in Canada.in which year was “Highway Patrolman” released? The answer is 1982.who produced “all or nothin’ at all”? The answer is Jon Landau, Chuck Plotkin, Bruce Springsteen and Roy Bittan. Navigate to the chat window and type a hash (#). A popup window is shown where you can select a knowledge base. Select the brucespringsteen collection. Now, you can start asking the questions from above. Question 1 The answer is correct. Also, note that the source where the information is found is mentioned. This is very convenient, as you will notice shortly. At the bottom, some controls are available to copy the response, to read the response aloud, to retrieve some information like token usage, and so on. Question 2 The response to question 2 is correct. According to the context provided, the song “Greetings from Asbury Park, N.J.” by Bruce Springsteen had a chart position of 60 in the US. Question 3 This question is a tricky one because an album called Tracks exists, but also an album called Tracks 18. Besides that, the album Tracks did not chart at all in Canada. The response is not wrong, but also not entirely correct. However, you can take a look at the sources and verify the response. Unfortunately, I don’t have information on the specific chart performance of the album “Tracks” in Canada from the provided context. Can you please provide more information or clarify what’s being asked? Question 4 The response to question 4 is correct. According to the context provided, “Highway Patrolman” by Bruce Springsteen was released in 1982. Question 5 The answer to question 5 is not correct. I’m not aware of any information about a song called “All or Nothin’ at All.” The context only mentions that it is part of the album “Human Touch” by Bruce Springsteen, but it doesn’t specify who produced it. Take a closer look at the sources. When adding a document to the knowledge base, the document is split into chunks. Because the documents contain tables, the header information is lost in many chunks, and therefore, the meaning of each cell is lost. That is probably why the correct chunk scores lower than 50%, and the model thinks this information is not relevant. Considering all responses, the result is certainly not bad because tables are quite challenging to use in RAG. The results in the previous blog were better, but there custom chunking was applied. Do note that in both cases the same model has been used. Web Search Another way to enrich the prompt with extra information is to add web pages. Create a new chat window, and just like adding documents, you can add a web page by typing a hash (#) and typing or pasting a web URL. Again, a popup window is shown, where you can select and add the web page. This works similarly to the knowledge you saw earlier. Add a previous blog: https://dzone.com/articles/why-mvn-install-may-risk-your-builds. Prompt to create a summary. The response is not correct. The blog is about Maven, but the summary is entirely wrong. Response Based on the provided context, it appears that the user is asking me to summarize the output of a Maven build process. From the context, it seems like there were two builds executed: one that was successful and another that failed due to dependency issues. The successful build took around 1 second to complete, while the failed build took significantly less time (0.175 seconds). The failed build had dependency issues, as indicated by the “FAILURE” message. However, without more specific information about what the user wants me to summarize (e.g., specific errors or metrics), it’s difficult to provide a more detailed summary. Can you please clarify what kind of summary you’re looking for? Are there any specific aspects of the build output you’d like me to highlight? Second attempt with the following prompt: Create a summary of the added blog post. Again, the model does not seem to understand what is asked for. Response Unfortunately, I don’t have enough information about the content of the added blog post. The provided context appears to be from a developer’s blog or website, but it doesn’t include any details about the actual blog post being referred to. Could you please provide more context or information about what the blog post is about? Is it related to Maven, Java development, or something else entirely? I’ll do my best to summarize the content based on your query. The third attempt is to explicitly add the link in the prompt (and add please :-)): Please create a summary of the blog https://mydeveloperplanet.com/2024/11/20/why-mvn-install-may-risk-your-builds/. This response is not entirely correct, but it is not wrong either. However, adding the link to the prompt did make a difference. In order to get a better result, you could change the underlying model. Anyway, Open WebUI did the job well. Response The blog post “Why ‘mvn install’ May Risk Your Builds” discusses potential risks associated with using the mvn install command in Maven build processes. According to the author, mvn install can cause issues when used on feature branches or branches that are not intended for production. This is because mvn install downloads and installs dependencies from the repository into the local Maven cache, which can lead to: Unfinished work being promoted to a more stable branch without proper testing.Dependencies from unstable features being installed in production environments. The author recommends using mvn verify instead of mvn install, as it verifies the build and checks for any dependencies issues before proceeding. Additionally, the blog post discusses the importance of considering separate stages in a build pipeline and using commands like rm -r ~/.m2/repository/com/mydeveloperplanet/mymavenverifyinstallplanet/ to clean up the local Maven repository between builds. Overall, the author aims to highlight the potential risks of relying on mvn install in certain situations and provide guidance on how to mitigate these risks. Knowledge PDF You can also add PDF documents to the knowledge base. Let’s find out whether information from a PDF can be retrieved. Again, information about Bruce Springsteen will be prompted. The following prompt will be used. The expected response is shown also. Who was Bruce Springsteen married to? Bruce Springsteen has been married to Julianne Phillips and to Patti Scialfa. First, let’s ask llama3.2 this question to see what the model knows already. The answer is partially correct. Patti Scialfa is mentioned, but not Julianne Phillips.The names of the children are correct, but none of the dates are correct. Response Bruce Springsteen has been married to Patti Scialfa since October 6, 1991. They have three children together: Evan James Springsteen (born March 25, 1990), Jessica Rae Springsteen (born April 30, 1992), and Sam Ryan Springsteen (born August 5, 2003). The information about his personal life can be found on the Bruce Springsteen Wikipedia page. The page is downloaded as a PDF and added to a new knowledge base, brucewiki. Prompt the question while adding this knowledge base. The answer is not better. He has been married twice, but Julianne Phillips is not mentioned again.Patti Scialfa was his second wife, not his first.The name of the second wife is made up.The names of the other ladies are correct. Response According to various sources, Bruce Springsteen has been married twice: his first wife, Patti Scialfa, whom he married in 1997 and divorced in 2009his second wife, actress Ponselle, but this information is not found in the provided context. However, it’s worth noting that Bruce Springsteen also dated photographer Lynn Goldsmith and model Karen Darvin, as well as actress Joyce Hyser, in the past. If you’re looking for more specific information on his marriages or relationships, I’d be happy to try and help further. Just for fun, let’s see what a web search returns as a response. Add the Wikipedia link and use the following prompt: Who was Bruce Springsteen married to, use the information from https://en.wikipedia.org/wiki/Bruce_Springsteen. The response is getting better. Springsteen did not divorce from Patti Scialfa, though.This time, Julianne Phillips is mentioned.The dates are wrong again. Response According to Wikipedia, Bruce Springsteen was married to Patti Scialfa from 1997 to 2015 [1]. They were together for over 18 years before their divorce. Before marrying Patti, he was married to Julianne Phillips from 1983 to 1989. It’s worth noting that his ex-wife Patti Scialfa has been a significant influence on his music and career, and has also pursued a successful music career in her own right. Controls At the top right corner, you can set some Controls. The System Prompt and Temperature are typically items you would like to change and control now and then. Be aware that you have this possibility. Admin Panel When you are an admin user, you can click your account, and you will be able to select the Admin Panel. You can add roles, set up LDAP, and many other administration tasks can be configured here. Conclusion Open WebUI is a very feature-rich UI that works entirely offline. As seen in the previous examples, using RAG has some issues, but this has nothing to do with Open WebUI. You can add custom logic to improve the results using Pipes. Besides that, you can experiment with different models and change parameters in order to improve the results.
Microsoft CEO Satya Nadella recently announced that GitHub Copilot is now free for all developers in VSCode. This is a game-changer in the software development industry. Github Copilot is an AI code assistant that helps developers finish their coding tasks easily and quickly. It also helps suggest code snippets and autocomplete functions. In this article, we will learn how to use GitHub Copilot using VSCode in a step-by-step manner for creating the first stateless flask microservice. This is a beginner-friendly guide showcasing how Copilot helps reduce the development time and simplify the process. Setting Up the Environment Locally As our primary focus will be on GitHub Copilot, I will write a high level on the software installation needed. If any installation issues are seen, it is expected that readers would have to solve them locally or comment in this article, where I can try to help. 1. Install Visual Studio Code on Mac or Windows from VSCode (In my examples, I used Mac). 2. Install GitHub Copilot extension in VSCode: Open VSCode and navigate to the Extensions view on the left, as per the below screenshot. Search for "copilot," and GitHub Copilot will appear. Click install. With this step, the Copilot extension is added to VSCode. 3. Activate the Copilot: If you do not have a GitHub account, please create one in GitHub.Back to VSCode, after installing Copilot, we can see in the Welcome tab that it will ask to sign up. Sign up using a GitHub account. Click "Chat with Copilot," and you will see the right side of VSCode, Copilot appears. Click "Chat with Copilot." We will see that the Copilot chat appears on the right-hand side of the VSCode palate. 4. Install Python in your system from Python based on Windows/Mac. Note that we are not installing Flask now; we will do it in a later step while installing the application to run. Writing the Microservice Using CoPilot 1. In VSCode, on the right side with the Copilot pallet, under "Ask Copilot," type: Create a Flask app. There are two ways we can ask Copilot. One is to create the Flask project folder with files and ask Copilot to add the code. Or, start from nothing and ask to create a Flask app. We notice that it will create a workspace for us along with all file creation, which is awesome, and the project gets created with the required files within a few seconds. Click -> Create Workspace -> give the location to save the project. The project will appear in VSCode. 2. We see that the project-created files will have routes.py, where a few default APIs are already generated. Now, we will create 2 APIs using Copilot. The first API is simple and used to greet a person. It takes the name of the person as input and out as "Hello, {name}." Open the routes.py file and add a comment as below: As soon as we hit enter, we see the code generated. Now press the tab, and we will see that the API code is generated. That's the advantage of using Copilot. Similarly, let's create another simple API that takes two integer values as input and returns the multiplication by using Copilot. This time we will try it in the right pallet of VSCode rather than in the routes.py file. Python # Create an endpoint to multiply two numbers. @main.route('/multiply') def multiply(): try: num1 = float(request.args.get('num1')) num2 = float(request.args.get('num2')) result = num1 * num2 return f'The result of {num1} * {num2} is {result}' except (TypeError, ValueError): return 'Invalid input. Please provide two numbers as query parameters.' However, I see a different code was generated when I asked Copilot to write the API inside the routes.py file. See below: Python # Create an endpoint to multiply two numbers. @main.route('/multiply/<int:num1>/<int:num2>') def multiply(num1, num2): return f'{num1} * {num2} = {num1 * num2}' The reason here is based on the previous context it generates the code. When we were on the routes.py file and asked the Copilot to generate the API code, it generated based on the context that the API should have two inputs and return the output. But when we requested to generate in the right palate, it generated based on the previous question with the context that it's a flak app, and input will come from the request param. So here, we can safely conclude that based on previous context, it will generate the next output. Now, both our APIs are ready, so let's deploy the app and test it. But we have not installed Flask yet. So, let's do that. 1. Activate the virtual environment and install Flask. Plain Text source venv/bin/activate # On Linux/Mac venv\Scripts\activate # On Windows pip install flask When we run the application, we see an issue in the startup due to the generated code. Below is the error: Plain Text File "/Users/sibasispadhi/Documents/coding/my-flask-app/venv/lib/python3.12/site-packages/flask/cli.py", line 72, in find_best_app app = app_factory() ^^^^^^^^^^^^^ File "/Users/sibasispadhi/Documents/coding/my-flask-app/app/__init__.py", line 14, in create_app app.register_blueprint(routes.bp) ^^^^^^^^^ AttributeError: module 'app.routes' has no attribute 'bp' (venv) sibasispadhi@Sibasiss-Air my-flask-app % The create_app function in our project's app/__init__.py file is calling app.register_blueprint(routes.bp), but the routes.py file doesn’t have bp (Blueprint object) defined. Below are the changes done to fix the problem. (See the code commented is the one autogenerated). Python # Register blueprints from . import routes # app.register_blueprint(routes.bp) app.register_blueprint(routes.main) Re-running the application will successfully deploy the application, and we are ready to test the functionality. The APIs can be tested using Postman. 2. Testing through Postman gives the results successfully. Conclusion GitHub Copilot generates the project and the boilerplate code seamlessly and it saves development time and effort. It's always advised to review the generated code so that it matches developers' expectations. Whenever there is an error, we must debug or request Copilot further suggestions to solve the problem. In this project, Copilot helped us create and run a stateless Flask microservice in no time. We faced some initial hiccups, which were solved after debugging, but overall, the development time was faster. I would suggest all readers start exploring Copilot today and enhance their day-to-day productivity. Stay tuned for my next set of articles on Copilot, where we will dive deep into more real-world scenarios and see how it solves our day-to-day tasks in a smooth manner.
As the Trump administration revokes Executive Order 14110, the U.S. shifts toward a market-driven AI strategy, departing from the Biden administration’s regulatory framework. While proponents see this as a catalyst for innovation and economic growth, critics warn of increased risks, regulatory fragmentation, and strained transatlantic relations. With Europe reinforcing its AI Act and states like California exploring their own regulations, the future of AI governance in the U.S. remains uncertain. Will deregulation accelerate progress, or does it open the door to new challenges in ethics, security, and global cooperation? Just days after taking office, Donald Trump, the 47th President of the United States, issued a series of executive actions aimed at dismantling key initiatives from the Biden administration. Among them was the revocation of Executive Order (EO) 14110, a landmark policy that established a framework for AI governance and regulation. This decision marks a turning point in U.S. AI policy. For its supporters, it is a necessary reform; for its critics, it is a dangerous setback. While EO 14110 aimed to structure AI adoption by balancing innovation and oversight, its repeal raises critical questions about the future of AI in the United States and its global impact. Background on Executive Order 14110 Executive Order 14110 was issued on October 30, 2023, under the Biden administration. This major initiative aimed to regulate the development and deployment of artificial intelligence. Its goal was to balance innovation, security, and economic stability while ensuring that AI systems remained reliable, safe, and transparent. In the Biden administration’s vision, EO 14110 was designed to address key concerns such as algorithmic bias, misinformation, job displacement, and cybersecurity vulnerabilities. It was not intended to impose direct restrictions on the private sector but rather to establish security and ethical standards, particularly for AI used by federal agencies and in public sector contracts, while also influencing broader AI governance. From an international perspective, EO 14110 also aimed to strengthen the United States' role in global AI governance. It aligned with the European Union’s approach, particularly as the EU was developing its AI Act. The order was part of a broader transatlantic effort to establish ethical and security standards for AI. "Artificial Intelligence (AI) holds extraordinary potential for both promise and peril. Responsible AI use has the potential to help solve urgent challenges while making our world more prosperous, productive, innovative, and secure. At the same time, irresponsible use could exacerbate societal harms such as fraud, discrimination, bias, and disinformation; displace and disempower workers; stifle competition; and pose risks to national security." (EO 14110 - Section 1) EO 14110 as Part of a Broader AI Strategy: Continuity in Biden’s Policy It is important to understand that EO 14110 was not an isolated initiative. It was part of a broader strategy built on several existing frameworks and commitments. Blueprint for an AI Bill of Rights (2022). A foundational document outlining five key principles: safe and effective AI systems, protections against algorithmic discrimination, data privacy, transparency, and human alternatives. Voluntary AI Commitments (2023-2024). Major tech companies, including Google, OpenAI, and Microsoft, agreed to self-regulation measures focusing on AI transparency, security, and ethics. National Security AI Strategy (2024). The Biden administration made AI a priority in cybersecurity, military applications, and critical infrastructure protection. It is worth noting that even after the revocation of EO 14110, these initiatives remain in place, ensuring a degree of continuity in AI governance in the United States. Objectives and Scope of EO 14110 Executive Order 14110 pursued several strategic objectives aimed at regulating AI adoption while promoting innovation. It emphasized the security and reliability of AI systems by requiring robustness testing and risk assessments, particularly in sensitive areas such as cybersecurity and critical infrastructure. It also aimed to ensure fairness and combat bias by implementing protections against algorithmic discrimination and promoting ethical AI use in hiring, healthcare, and justice. EO 14110 included training, reskilling, and protection programs to help workers adapt to AI-driven changes. It also aimed to protect consumers by preventing fraudulent or harmful AI applications, ensuring safe and beneficial use. Finally, the executive order aimed to reinforce international cooperation, particularly with the European Union, to establish common AI governance standards. However, it’s important to note that it did not aim to regulate the entire private sector but rather to set strict ethical and security standards for AI systems used by federal agencies. Summary of EO 14110’s Key Principles To quickly get the essentials, here are the eight fundamental principles it was built on: Ensure AI is safe and secure.Promote responsible innovation and competition.Support workers affected by AI’s deployment.Advance equity and civil rights.Protect consumer interests in AI applications.Safeguard privacy and civil liberties.Enhance AI capabilities within the federal government.Promote U.S. leadership in global AI governance. Why Did the Trump Administration Revoke EO 14110? So, on January 20, 2025, the Trump administration announced the revocation of EO 14110, arguing that it restricted innovation by imposing excessive administrative constraints. The White House justified this decision as part of a broader push to deregulate the sector, boost the economy, and attract AI investment. The administration made clear its preference for a market-driven approach. According to Trump, private companies are better positioned to oversee AI development without federal intervention. Clearly, this shift marks a geopolitical turning point. The United States is moving away from a multilateral approach to assert its dominance in the AI sector. However, this revocation does not mean the end of AI regulation in the United States. Other federal initiatives, such as the NIST AI Risk Management Framework, remain in place. "Republicans support AI development rooted in free speech and human flourishing." (The 2024 Republican Party by Reuters) Consequences of the Revocation in the United States The repeal of EO 14110 has immediate effects and long-term implications. It reshapes the future of AI development in the United States. From the Trump administration’s perspective, this decision removes bureaucratic hurdles, accelerates innovation, and strengthens U.S. competitiveness in AI. Supporters argue that by reducing regulatory constraints, the repeal allows companies to move faster, lowers compliance costs, and attracts greater investment, particularly in automation and biotechnology. But on the other hand, without a federal framework, the risks associated with the development and use of AI technologies are increasing. Algorithmic bias, cybersecurity vulnerabilities, and the potential misuse of AI become harder to control without national oversight. Critics also warn of a weakening of worker and consumer protections, as the end of support programs could further deepen economic inequalities. In practical terms, regulation is becoming more fragmented. Without a federal framework, each state could, and likely will, develop its own AI laws, making compliance more complex for businesses operating nationwide. Some see this as an opportunity for regulatory experimentation, while others see it as a chance for opportunistic players to exploit loopholes or fear legal uncertainty and increased tensions with international partners. Impact on Europe The revocation of EO 14110 also affects global AI governance, particularly in Europe. Transatlantic relations are likely to become strained, as the growing divergence between U.S. and European approaches will make regulatory cooperation more challenging. European companies may tighten their compliance standards to maintain consumer trust, which could influence their strategic decisions. In fact, the European Union may face pressure to adjust its AI Act, although its regulatory framework remains largely independent from that of the United States. Conclusion The revocation of Executive Order 14110 is more than just a policy shift in the United States. It represents a strategic choice, favoring a deregulated model where innovation takes precedence over regulation. While this decision may help accelerate technological progress, it also leaves critical questions unanswered: Who will ensure the ethics, security, and transparency of AI in the United States? For Europe, this shift deepens the divide with the U.S. and strengthens its role as a "global regulator" through the AI Act. The European Union may find itself alone at the forefront of efforts to enforce strict AI regulations, risking a scenario where some companies favor the less restrictive U.S. market. More than a debate on regulation, this revocation raises a fundamental question: In the global AI race, should progress be pursued at all costs, or should every advancement be built on solid and ethical foundations? The choices made today will shape not only the future of the industry but also the role of democracies in the face of tech giants. One More Thing The revocation of EO 14110 highlights a broader debate: who really shapes AI policy, the government or private interests? While the U.S. moves toward deregulation, California’s AI safety bill (SB 1047) is taking the opposite approach, proposing strict oversight for advanced AI models. But as an investigation by Pirate Wires reveals, this push for regulation isn’t without controversy. Dan Hendrycks, a key advocate for AI safety, co-founded Gray Swan, a company developing compliance tools that could directly benefit from SB 1047’s mandates. This raises a crucial question: When policymakers and industry leaders are deeply intertwined, is AI regulation truly about safety, or about controlling the market? In the race to govern AI, transparency may be just as important as regulation itself.
Top 5 GRC Certifications for Cybersecurity Professionals
February 11, 2025 by
The Impact of Asynchronous Work on Engineering Innovation
February 11, 2025 by
Productivity and Organization Tips for Software Engineers
February 4, 2025
by
CORE
System Design of an Audio Streaming Service
February 11, 2025 by
From Zero to Scale With AWS Serverless
February 11, 2025 by
Understanding AWS Karpenter for Kubernetes Auto-Scaling
February 11, 2025
by
CORE
System Design of an Audio Streaming Service
February 11, 2025 by
From Zero to Scale With AWS Serverless
February 11, 2025 by
Understanding AWS Karpenter for Kubernetes Auto-Scaling
February 11, 2025
by
CORE
From Zero to Scale With AWS Serverless
February 11, 2025 by
Understanding AWS Karpenter for Kubernetes Auto-Scaling
February 11, 2025
by
CORE
Logfire: Uncomplicated Observability for Python Applications
February 11, 2025
by
CORE
From Zero to Scale With AWS Serverless
February 11, 2025 by
Understanding AWS Karpenter for Kubernetes Auto-Scaling
February 11, 2025
by
CORE
Logfire: Uncomplicated Observability for Python Applications
February 11, 2025
by
CORE
Chat Completion Models vs OpenAI Assistants API
February 11, 2025 by
SQL as the Backbone of Big Data and AI Powerhouses
February 11, 2025 by
Perfecting CRUD Functionality in NextJS
February 11, 2025 by